1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import hilog from '@ohos.hilog'; 16import fs from '@ohos.file.fs'; 17import type { Filter } from '@ohos.file.fs'; 18import fileExtensionInfo from '@ohos.file.fileExtensionInfo'; 19import type { Fileinfo } from './Common'; 20import { getPath, uriReturnObject, encodePathOfUri, DOMAIN_CODE, TAG, infosReturnObject } from './Common'; 21 22const documentFlag = fileExtensionInfo.DocumentFlag; 23const ERR_OK = 0; 24const E_GETRESULT = 14300004; 25const APP_DATA = 'appdata'; 26const BACKUP_DIR = '.backup'; 27const CURRENT_USER_PATH = '/storage/Users/currentUser'; 28 29function hasFilter(filter: Filter) : boolean { 30 if (filter === null) { 31 return false; 32 } 33 let displayNameArray = filter.displayName; 34 if (displayNameArray !== null && displayNameArray.length > 0) { 35 return true; 36 } 37 let suffixArray = filter.suffix; 38 if (suffixArray !== null && suffixArray.length > 0) { 39 return true; 40 } 41 let lastModifiedAfter = filter.lastModifiedAfter; 42 if (lastModifiedAfter !== null && lastModifiedAfter >= 0) { 43 return true; 44 } 45 let fileSizeOver = filter.fileSizeOver; 46 if (fileSizeOver !== null && fileSizeOver >= 0) { 47 return true; 48 } 49 return false; 50} 51 52function buildDisplayName(displayNameArray: string[]) : string[] { 53 let displayNames : string[] = []; 54 for (let i = 0; i < displayNameArray.length; i++) { 55 if (displayNameArray[i].lastIndexOf('*') === -1) { 56 let name = '*' + displayNameArray[i]; 57 displayNames.push(name); 58 } else { 59 displayNames.push(displayNameArray[i]); 60 } 61 } 62 return displayNames; 63} 64 65function buildFilterOptions(filter: Filter, listNum: number, recursion: boolean) : 66{recursion: boolean, listNum: number, filter: Filter} { 67 let optionFilter: Filter = {}; 68 if (filter !== null) { 69 let suffixArray = filter.suffix; 70 if (suffixArray !== null && suffixArray.length > 0) { 71 optionFilter.suffix = suffixArray; 72 } 73 let displayNameArray = filter.displayName; 74 if (displayNameArray !== null && displayNameArray.length > 0) { 75 optionFilter.displayName = buildDisplayName(displayNameArray); 76 } 77 let fileSizeOver = filter.fileSizeOver; 78 if (fileSizeOver !== null && fileSizeOver >= 0) { 79 optionFilter.fileSizeOver = fileSizeOver; 80 } 81 let lastModifiedAfter = filter.lastModifiedAfter; 82 if (lastModifiedAfter !== null && lastModifiedAfter >= 0) { 83 optionFilter.lastModifiedAfter = lastModifiedAfter; 84 } 85 let mimeType = filter.mimeType; 86 if (mimeType !== null && mimeType.length > 0) { 87 hilog.error(DOMAIN_CODE, TAG, 'mimeType is not supported as a filter condition'); 88 } 89 let excludeMedia = filter.excludeMedia; 90 if (excludeMedia !== null && excludeMedia === true) { 91 hilog.error(DOMAIN_CODE, TAG, 'excludeMedia is not supported as a filter condition'); 92 } 93 } 94 let options = { 95 'recursion': recursion, 96 'listNum': listNum, 97 'filter': optionFilter, 98 }; 99 return options; 100} 101 102function buildNoFilterOptions(listNum: number, recursion: boolean) : {recursion: boolean, listNum: number} { 103 let options = { 104 'recursion': recursion, 105 'listNum': listNum, 106 }; 107 return options; 108} 109 110function getNewPathOrUri(prefixSection: string, filename: string) : string { 111 let completeResult = prefixSection; 112 if (completeResult.endsWith('/')) { 113 if (filename.startsWith('/')) { 114 filename = filename.substring(1, filename.length); 115 } 116 completeResult += filename; 117 } else { 118 if (!filename.startsWith('/')) { 119 completeResult += '/'; 120 } 121 completeResult += filename; 122 } 123 return completeResult; 124} 125 126function genNewFileName(filename: string): string { 127 let newFilename = filename; 128 let index = newFilename.lastIndexOf('/'); 129 if (index !== -1) { 130 newFilename = newFilename.substring(index + 1, newFilename.length); 131 } 132 return newFilename; 133} 134 135function getListFileInfos(sourceFileUri: string, offset: number, count: number, filter: Filter, recursion: boolean) : 136{infos: Fileinfo[], code: number} { 137 let infos : Fileinfo[] = []; 138 let path = getPath(sourceFileUri); 139 try { 140 let statPath = fs.statSync(path); 141 if (!statPath.isDirectory()) { 142 return infosReturnObject([], E_GETRESULT); 143 } 144 let options; 145 let listNum = offset + count; 146 if (hasFilter(filter)) { 147 options = buildFilterOptions(filter, listNum, recursion); 148 } else { 149 options = buildNoFilterOptions(listNum, recursion); 150 } 151 let fileNameList = fs.listFileSync(path, options); 152 for (let i = offset; i < fileNameList.length; i++) { 153 if (i === listNum) { 154 break; 155 } 156 if (path === CURRENT_USER_PATH && (fileNameList[i] === APP_DATA || fileNameList[i] === BACKUP_DIR)) { 157 hilog.info(DOMAIN_CODE, TAG, `filter appdata doc or backup dir`); 158 continue; 159 } 160 let mode = documentFlag.SUPPORTS_READ | documentFlag.SUPPORTS_WRITE; 161 let filePath = getNewPathOrUri(path, fileNameList[i]); 162 let stat = fs.statSync(filePath); 163 if (stat.isDirectory()) { 164 mode |= documentFlag.REPRESENTS_DIR; 165 } else { 166 mode |= documentFlag.REPRESENTS_FILE; 167 } 168 let newFileUri = getNewPathOrUri(sourceFileUri, fileNameList[i]); 169 newFileUri = encodePathOfUri(newFileUri); 170 infos.push({ uri: newFileUri, relativePath: filePath, fileName: genNewFileName(fileNameList[i]), 171 mode: mode, size: stat.size, mtime: stat.mtime, mimeType: '' }); 172 } 173 } catch (e) { 174 hilog.error(DOMAIN_CODE, TAG, `getFileInfos error: ${e.message},code: ${e.code}`); 175 return infosReturnObject([], E_GETRESULT); 176 } 177 return infosReturnObject(infos, ERR_OK); 178} 179 180function getSubUriList(path: string, listNum: number, filter: Filter) : string[] 181{ 182 let dirOptions = { 183 'recursion': false 184 }; 185 let fileOption = hasFilter(filter) ? 186 buildFilterOptions(filter, listNum, false) : buildNoFilterOptions(listNum, false); 187 let dirTmpResult = fs.listFileSync(path, dirOptions).filter(item => item !== APP_DATA).map(function(item) { 188 return CURRENT_USER_PATH + '/' + item; 189 }); 190 let fileResult = fs.listFileSync(path, fileOption).filter(item => item !== APP_DATA).map(function(item) { 191 return CURRENT_USER_PATH + '/' + item; 192 }); 193 let dirResult : string[] = []; 194 for (let i = 0; i < dirTmpResult.length; ++i) { 195 if (fs.statSync(dirTmpResult[i]).isDirectory()) { 196 dirResult.push(dirTmpResult[i]); 197 } 198 } 199 return Array.from(new Set(fileResult.concat(dirResult))); 200} 201 202function getSubFileInfos( 203 changeData: { 204 options: {recursion: boolean, listNum: number, filter?: Filter}, 205 tempOffset: number, 206 listNumCnt: number 207 }, needInfo: {subPath: string, count: number, isRootPath: boolean, sourceFileUri: string}): Fileinfo[] { 208 let infos: Fileinfo[] = []; 209 let tmpStat = fs.statSync(needInfo.subPath); 210 let bIsDct = tmpStat.isDirectory(); 211 let fileNameList = bIsDct ? fs.listFileSync(needInfo.subPath, changeData.options) : 212 [needInfo.subPath.substring(CURRENT_USER_PATH.length)]; 213 let subPath = needInfo.subPath; 214 if (needInfo.isRootPath) { 215 subPath = bIsDct ? subPath.substring(CURRENT_USER_PATH.length) : ''; 216 } 217 let listLen = fileNameList.length; 218 if (changeData.tempOffset >= listLen) { 219 changeData.tempOffset -= listLen; 220 return infos; 221 } 222 for (let j = changeData.tempOffset; j < fileNameList.length; ++j, ++changeData.listNumCnt) { 223 if (changeData.listNumCnt === needInfo.count) { 224 break; 225 } 226 let mode = documentFlag.SUPPORTS_READ | documentFlag.SUPPORTS_WRITE; 227 let filePath = getNewPathOrUri(needInfo.subPath, fileNameList[j]); 228 let stat = bIsDct ? fs.statSync(filePath) : tmpStat; 229 mode |= (bIsDct | stat.isDirectory()) ? documentFlag.REPRESENTS_DIR : documentFlag.REPRESENTS_FILE; 230 let newFileUri = getNewPathOrUri( 231 needInfo.isRootPath ? needInfo.sourceFileUri + subPath : needInfo.sourceFileUri, fileNameList[j]); 232 newFileUri = encodePathOfUri(newFileUri); 233 infos.push({ uri: newFileUri, relativePath: filePath, fileName: genNewFileName(fileNameList[j]), 234 mode: mode, size: stat.size, mtime: stat.mtime, mimeType: '' }); 235 } 236 changeData.tempOffset = 0; 237 changeData.options.listNum = needInfo.count - changeData.listNumCnt; 238 return infos; 239} 240 241function getScanFileInfos(sourceFileUri: string, offset: number, count: number, filter: Filter, recursion: boolean) : 242{infos: Fileinfo[], code: number} { 243 let infos : Fileinfo[] = []; 244 let path = getPath(sourceFileUri); 245 try { 246 let statPath = fs.statSync(path); 247 if (!statPath.isDirectory()) { 248 return infosReturnObject([], E_GETRESULT); 249 } 250 let listNum = offset + count; 251 let isRootPath = (path === CURRENT_USER_PATH); 252 let listInfo = (isRootPath) ? getSubUriList(path, listNum, filter) : [path]; 253 let changeData = { 254 options: hasFilter(filter) ? 255 buildFilterOptions(filter, listNum, recursion) : buildNoFilterOptions(listNum, recursion), 256 tempOffset: offset, 257 listNumCnt: 0 258 }; 259 for (let i = 0; i < listInfo.length; ++i) { 260 const needInfo = { 261 subPath: listInfo[i], 262 count: count, 263 isRootPath: isRootPath, 264 sourceFileUri: sourceFileUri 265 }; 266 let subFileRes = getSubFileInfos(changeData, needInfo); 267 infos.push(...subFileRes); 268 if (changeData.options.listNum <= 0) { 269 break; 270 } 271 } 272 } catch (e) { 273 hilog.error(DOMAIN_CODE, TAG, `getFileInfos error: ${e.message},code: ${e.code}`); 274 return infosReturnObject([], E_GETRESULT); 275 } 276 return infosReturnObject(infos, ERR_OK); 277} 278export { getListFileInfos, getScanFileInfos, buildFilterOptions, buildNoFilterOptions, hasFilter }; 279