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