1/* 2 * Copyright (c) 2021-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 */ 15 16import { fileTree } from './component/dialog/FileMoveDialog' 17import Logger from '../base/log/Logger' 18import ErrorCodeConst from '../base//constants/ErrorCodeConst' 19import { toast, setSystemBar, setImmersion } from '../base/utils/Common' 20import AbilityCommonUtil from '../base/utils/AbilityCommonUtil' 21import { SYSTEM_BAR_COLOR } from '../base/constants/UiConstant' 22import StringUtil from '../base/utils/StringUtil' 23import { FileUtil } from '../base/utils/FileUtil' 24import ObjectUtil from '../base/utils/ObjectUtil' 25import MediaLibrary from '@ohos.multimedia.mediaLibrary'; 26import fileAccess from '@ohos.file.fileAccess' 27import { ArrayUtil } from '../base/utils/ArrayUtil' 28import { UiUtil } from '../base/utils/UiUtil' 29 30const TAG = 'PathSelector' 31 32@Entry 33@Component 34struct PathSelector { 35 36 @State createResultType:number = ErrorCodeConst.PICKER.NORMAL; 37 38 aboutToAppear(){ 39 UiUtil.setWindowBackground(SYSTEM_BAR_COLOR.LIGHT_GRAY); 40 } 41 42 async saveFileCallback(res): Promise<void> { 43 if (res?.cancel) { 44 globalThis.pathAbilityContext.terminateSelf() 45 return 46 } else { 47 let fileNameList = globalThis.keyPickFileName 48 // 保存单个文件时文件名可修改,需使用修改后的文件名来创建文件 49 if (fileNameList.length <= 1) { 50 fileNameList = [res.fileName] 51 } 52 this.saveFiles(res.selectUri, fileNameList).then((createdFileList) => { 53 AbilityCommonUtil.terminatePathPicker(createdFileList) 54 }).catch((err) => { 55 let errorMessage = '' 56 let errorCode = 0 57 Logger.e(TAG, JSON.stringify(err)); 58 if (err.code) { 59 if (err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_EXIST) { 60 errorMessage = 'Same name file already exists' 61 errorCode = ErrorCodeConst.PICKER.FILE_NAME_EXIST 62 this.createResultType = errorCode; 63 const pathName = globalThis.keyPickFileName; 64 let listLength:number = pathName.length; 65 if(listLength == 1){ 66 return; 67 } 68 } else if (err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_INVALID) { 69 errorMessage = 'Invalid display name' 70 errorCode = ErrorCodeConst.PICKER.FILE_NAME_INVALID 71 } else { 72 errorMessage = 'File create failed' 73 errorCode = ErrorCodeConst.PICKER.OTHER_ERROR 74 } 75 } else { 76 errorMessage = err.message ? err.message : err 77 errorCode = ErrorCodeConst.PICKER.OTHER_ERROR 78 } 79 AbilityCommonUtil.terminatePathPicker([], errorCode, errorMessage) 80 toast($r('app.string.save_file_fail')) 81 Logger.e(TAG, `path select error, errorCode: ${errorCode}, errorMessage: ${errorMessage}`) 82 }) 83 } 84 } 85 86 /** 87 * PathPicker保存文件 88 * @param data SaveFilesParam 89 */ 90 async saveFiles(path: string, nameList: Array<string>): Promise<Array<string>> { 91 return new Promise(async (resolve, reject) => { 92 let fileAccessHelper = await FileUtil.getFileAccessHelperAsync(globalThis.abilityContext); 93 let dirPath = path; 94 if (StringUtil.isEmpty(dirPath)) { 95 dirPath = (await FileUtil.getFileInfoByRelativePath('Documents/', fileAccessHelper)).uri; 96 } 97 let fileNameArr = nameList; 98 let successArr: Array<string> = []; 99 let resultErr: any; 100 let len: number = fileNameArr.length; 101 let fileNameList: string[] = []; 102 if (len > 1) { 103 fileNameList = await this.getPickPathListFiles(dirPath, fileAccessHelper); 104 } 105 Logger.i(TAG, 'saveFiles createName: ' + JSON.stringify(fileNameArr)+" ; "); 106 Logger.i(TAG, 'saveFiles subList: ' + JSON.stringify(fileNameList)+" ; "); 107 for (let i = 0; i < len; i++) { 108 const currName = fileNameArr[i]; 109 let result 110 if (len === 1) { 111 result = await FileUtil.createFile(fileAccessHelper, dirPath, currName); 112 } else { 113 result = await this.tryRenameFileOperate(fileAccessHelper, currName, dirPath, 0, fileNameList); 114 } 115 if (ObjectUtil.isUndefined(result.err)) { 116 Logger.i(TAG, "saveFiles createOK: " + result.uri); 117 successArr.push(result.uri); 118 continue; 119 } 120 Logger.i(TAG, 'saveFiles err: ' + result.err.code); 121 // 失败 122 resultErr = { code: result.err.code, message: result.err.message }; 123 let mediaLibrary; 124 try { 125 mediaLibrary = MediaLibrary.getMediaLibrary(globalThis.abilityContext); 126 } catch (error) { 127 Logger.e(TAG, 'getMediaLibrary fail, error:' + JSON.stringify(error)) 128 } 129 if (ObjectUtil.isNullOrUndefined(mediaLibrary)) { 130 break; 131 } 132 for (let i = 0; i < successArr.length; i++) { 133 await FileUtil.hardDelete(successArr[i], mediaLibrary); 134 } 135 try { 136 mediaLibrary.release(); 137 } catch (e) { 138 Logger.e(TAG, 'mediaLibrary close error') 139 } 140 successArr = []; 141 break; 142 } 143 144 Logger.i(TAG, 'saveFiles end: ' + JSON.stringify(successArr)); 145 if (!ArrayUtil.isEmpty(successArr)) { 146 resolve(successArr); 147 } else { 148 reject(resultErr); 149 } 150 }) 151 } 152 153 private async getPickPathListFiles(dirUri: string, fileAccessHelper: fileAccess.FileAccessHelper): Promise<string[]> { 154 let fileInfo: fileAccess.FileInfo = await FileUtil.getFileInfoByUri(dirUri, fileAccessHelper); 155 if (ObjectUtil.isNullOrUndefined(fileInfo) || !FileUtil.isFolder(fileInfo.mode)) { 156 return []; 157 } 158 return this.getFilesByIterator(fileInfo.listFile()); 159 } 160 161 private getFilesByIterator(fileIterator: fileAccess.FileIterator): string[] { 162 if (ObjectUtil.isNull(fileIterator)) { 163 return null; 164 } 165 let result: Array<string> = new Array(); 166 let isDone = false; 167 while (!isDone) { 168 try { 169 let nextFileInfo = fileIterator.next(); 170 isDone = nextFileInfo.done; 171 if (isDone) { 172 break; 173 } 174 let currFile = nextFileInfo.value; 175 if (!FileUtil.isFolder(currFile.mode)) { 176 result.push(currFile.fileName); 177 } 178 } catch (err) { 179 Logger.e(TAG, 'current File err: ' + JSON.stringify(err) + ', ' + err.toString()); 180 } 181 } 182 return result; 183 } 184 185 private async tryRenameFileOperate(fileAccessHelper: fileAccess.FileAccessHelper, fileName: string, dirUri: string, renameCount: number, fileNameList: string[] = []): Promise<{ err,uri }> { 186 let index = fileName.lastIndexOf('.'); 187 let name = fileName; 188 let suffix = ''; 189 if (index !== -1) { 190 suffix = fileName.substring(index, fileName.length); 191 name = fileName.substring(0, index); 192 } 193 let hasReNameCount = FileUtil.getFileNameReName(name); 194 if (!ObjectUtil.isNullOrUndefined(hasReNameCount)) { 195 let num = Number(hasReNameCount[1]); 196 if (!isNaN(num)) { 197 name = hasReNameCount[0]; 198 renameCount = num; 199 } 200 } 201 202 let newName = fileName; 203 while (true) { 204 newName = FileUtil.renameFile(name, renameCount++, suffix); 205 let index = this.getIndex(newName, fileNameList); 206 Logger.i(TAG, "tryRenameFileOperate : " + newName + " ; index = " + index); 207 if (index === -1) { 208 const result = await FileUtil.createFile(fileAccessHelper, dirUri, newName); 209 if (ObjectUtil.isUndefined(result.err)) { 210 Logger.i(TAG, "tryRenameFileOperate createOK: " + result.uri); 211 return result; 212 } else { 213 Logger.i(TAG, "tryRenameFileOperate createFail: " + JSON.stringify(result) + " ; " + newName); 214 if (result.err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_EXIST) { 215 fileNameList.push(newName); 216 } else { 217 return result; 218 } 219 } 220 } 221 } 222 } 223 224 private getIndex(fileName: string, fileNameList: string[] = []) { 225 return fileNameList.findIndex(value => value === fileName); 226 } 227 228 build() { 229 Row() { 230 fileTree({ 231 createFileFailType: $createResultType, 232 moveCallback: (e) => { 233 this.saveFileCallback(e) 234 } 235 }) 236 } 237 } 238} 239