* Copyright (C) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "media_uri_utils.h"
#include "media_log.h"
#include "media_string_utils.h"
#include "media_path_utils.h"
#include "media_pure_file_utils.h"
#include "medialibrary_errno.h"
namespace OHOS::Media {
const int ASSET_IN_BUCKET_NUM_MAX = 1000;
const int ASSET_DIR_START_NUM = 16;
const int ASSET_MAX_NUM = 10000000;
const int DEFAULT_USER_ID_INNER = -1;
const std::string PHOTO_BUCKET_INNER = "Photo";
const std::string ROOT_MEDIA_DIR = "/storage/cloud/files";
const std::string URI_PREFIX_INNER = "file://media/";
const std::string PHOTO_URI_PREFIX_INNER = "file://media/Photo/";
const std::string MULTI_USER_URI_FLAG = "user";
const std::string GARBLE = "*";
const std::string SPLIT_PATH = "/";
const std::string DOT = ".";
constexpr uint32_t GARBLE_SMALL = 3;
constexpr uint32_t GARBLE_LARGE = 8;
constexpr uint32_t GARBLE_LAST_TWO = 2;
constexpr uint32_t GARBLE_LAST_ONE = 1;
void MediaUriUtils::AppendKeyValue(std::string &uri, const std::string &key, std::string value)
{
std::string uriKey = key + '=';
if (uri.find(uriKey) != std::string::npos) {
return;
}
char queryMark = (uri.find('?') == std::string::npos) ? '?' : '&';
std::string append = queryMark + key + '=' + value;
size_t pos = uri.find('#');
if (pos == std::string::npos) {
uri += append;
} else {
uri.insert(pos, append);
}
}
int32_t MediaUriUtils::CreateAssetBucket(int32_t fileId, int32_t &bucketNum)
{
if (fileId < 0) {
MEDIA_ERR_LOG("input fileId [%{public}d] is invalid", fileId);
return E_INVALID_FILEID;
}
fileId = fileId % ASSET_MAX_NUM;
int start = ASSET_DIR_START_NUM;
int divider = ASSET_DIR_START_NUM;
while (fileId > start * ASSET_IN_BUCKET_NUM_MAX) {
divider = start;
start *= 2;
}
int fileIdRemainder = fileId % divider;
if (fileIdRemainder == 0) {
bucketNum = start + fileIdRemainder;
} else {
bucketNum = (start - divider) + fileIdRemainder;
}
return E_OK;
}
std::string MediaUriUtils::GetUriByExtrConditions(const std::string &prefix, const std::string &fileId,
const std::string &suffix)
{
return prefix + fileId + suffix;
}
std::string MediaUriUtils::GetPathFromUri(const std::string &uri)
{
size_t index = uri.rfind('/');
if (index == std::string::npos) {
MEDIA_ERR_LOG("index invalid %{private}s", uri.c_str());
return "";
}
std::string realTitle = uri.substr(0, index);
index = realTitle.rfind('/');
if (index == std::string::npos) {
MEDIA_ERR_LOG("invalid realTitle %{private}s", uri.c_str());
return "";
}
realTitle = realTitle.substr(index + 1);
index = realTitle.rfind('_');
if (index == std::string::npos) {
MEDIA_ERR_LOG("realTitle can not find _ %{private}s", uri.c_str());
return "";
}
std::string fileId = realTitle.substr(index + 1);
int32_t fileUniqueId = 0;
bool result = MediaStringUtils::ConvertToInt(fileId, fileUniqueId);
if (!result) {
MEDIA_ERR_LOG("invalid fileuri %{private}s", uri.c_str());
return "";
}
int32_t bucketNum = 0;
CreateAssetBucket(fileUniqueId, bucketNum);
std::string ext = MediaPathUtils::GetExtension(uri);
if (ext.empty()) {
MEDIA_ERR_LOG("invalid ext %{private}s", uri.c_str());
return "";
}
std::string path = ROOT_MEDIA_DIR + "/" + PHOTO_BUCKET_INNER + "/" + std::to_string(bucketNum) + "/" + realTitle +
"." + ext;
if (!MediaPureFileUtils::IsFileExists(path)) {
MEDIA_ERR_LOG("file not exist, path=%{private}s", path.c_str());
return "";
}
return path;
}
int32_t MediaUriUtils::GetFileId(const std::string &uri)
{
const static int ERROR = -1;
if (PHOTO_URI_PREFIX_INNER.size() >= uri.size()) {
MEDIA_ERR_LOG("photo uri is too short");
return ERROR;
}
if (uri.substr(0, PHOTO_URI_PREFIX_INNER.size()) != PHOTO_URI_PREFIX_INNER) {
MEDIA_ERR_LOG("only photo uri is valid");
return ERROR;
}
std::string tmp = uri.substr(PHOTO_URI_PREFIX_INNER.size());
std::string fileIdStr = tmp.substr(0, tmp.find_first_of('/'));
int32_t value = 0;
bool result = MediaStringUtils::ConvertToInt(fileIdStr, value);
if (!result) {
MEDIA_ERR_LOG("asset fileId is invalid");
return ERROR;
}
return value;
}
std::string MediaUriUtils::GetFileIdStr(const std::string &uri)
{
const static std::string ERROR = "-1";
if (URI_PREFIX_INNER.size() >= uri.size()) {
MEDIA_ERR_LOG("uri is too short");
return ERROR;
}
if (uri.substr(0, URI_PREFIX_INNER.size()) != URI_PREFIX_INNER) {
MEDIA_ERR_LOG("invalid media uri prefix");
return ERROR;
}
std::string tmp = uri.substr(URI_PREFIX_INNER.size());
tmp = tmp.substr(tmp.find_first_of('/') + 1);
std::string fileIdStr = tmp.substr(0, tmp.find_first_of('/'));
if (fileIdStr.empty()) {
MEDIA_ERR_LOG("fileId is empty");
return ERROR;
}
return fileIdStr;
}
Uri MediaUriUtils::GetMultiUri(Uri &uri, int32_t userId)
{
if (userId == DEFAULT_USER_ID_INNER) {
return uri;
}
std::string uriString = uri.ToString();
AppendKeyValue(uriString, MULTI_USER_URI_FLAG, std::to_string(userId));
return Uri(uriString);
}
bool MediaUriUtils::CheckUri(const std::string &uri)
{
if (uri.find("../") != std::string::npos) {
return false;
}
std::string uriprex = "file://media";
return uri.substr(0, uriprex.size()) == uriprex;
}
static std::string GetTitleFromDisplayName(const std::string &displayName)
{
std::string title;
if (!displayName.empty()) {
std::string::size_type pos = displayName.find_last_of('.');
if (pos == std::string::npos) {
return "";
}
title = displayName.substr(0, pos);
}
return title;
}
std::string MediaUriUtils::GetSafeDiaplayName(const std::string &displayName)
{
if (displayName == "") {
return displayName;
}
std::string extension;
size_t splitIndex = displayName.find_last_of(DOT);
if (splitIndex == std::string::npos) {
extension = "";
} else {
extension = displayName.substr(splitIndex);
}
std::string title = GetTitleFromDisplayName(displayName);
if (title == "") {
return title;
}
uint32_t length = title.size();
std::string safeDisplayName;
if (length <= GARBLE_SMALL) {
safeDisplayName = GARBLE + title.substr(length - GARBLE_LAST_ONE) + extension;
} else if (length > GARBLE_LARGE) {
safeDisplayName = GARBLE + title.substr(GARBLE_LARGE) + extension;
} else {
safeDisplayName = GARBLE + title.substr(length - GARBLE_LAST_TWO) + extension;
}
return safeDisplayName;
}
std::string MediaUriUtils::GetSafeUri(const std::string &uri)
{
std::string safeUri = uri;
if (uri == "") {
return safeUri;
}
size_t splitIndex = safeUri.find_last_of(SPLIT_PATH);
std::string displayName;
if (splitIndex == std::string::npos) {
return safeUri;
} else {
displayName = safeUri.substr(splitIndex + 1);
}
std::string safeDisplayName = GetSafeDiaplayName(displayName);
safeUri = safeUri.substr(0, splitIndex) + "/" + safeDisplayName;
return safeUri;
}
}