1/* 2 * Copyright (c) 2023-2024 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 type { BusinessError } from '@ohos.base'; 17import fs from '@ohos.file.fs'; 18import fileuri from '@ohos.file.fileuri'; 19import type uri from '@ohos.uri'; 20import Logger from '../log/Logger'; 21 22export class FsUtil { 23 static readonly TAG: string = 'FsUtil'; 24 25 public static async stat(file: string | number): Promise<fs.Stat | BusinessError> { 26 try { 27 return await fs.stat(file); 28 } catch (error) { 29 Logger.i(FsUtil.TAG, 'fs stat error = ' + JSON.stringify(error)); 30 return error; 31 } 32 } 33 34 public static statSync(file: string | number): fs.Stat | BusinessError { 35 try { 36 return fs.statSync(file); 37 } catch (error) { 38 Logger.i(FsUtil.TAG, 'fs statSync error = ' + JSON.stringify(error)); 39 return error; 40 } 41 } 42 43 public static async access(path: string): Promise<boolean | BusinessError> { 44 try { 45 return await fs.access(path); 46 } catch (error) { 47 Logger.i(FsUtil.TAG, 'fs access error = ' + JSON.stringify(error)); 48 return error; 49 } 50 } 51 52 public static accessSync(path: string): boolean { 53 try { 54 return fs.accessSync(path); 55 } catch (error) { 56 Logger.i(FsUtil.TAG, 'fs accessSync error = ' + JSON.stringify(error)); 57 return false; 58 } 59 } 60 61 public static openSync(path: string, mode?: number): fs.File | BusinessError { 62 try { 63 return fs.openSync(path, mode); 64 } catch (error) { 65 Logger.i(FsUtil.TAG, 'fs openSync error = ' + JSON.stringify(error)); 66 return error; 67 } 68 } 69 70 public static async close(file: number | fs.File): Promise<void | BusinessError> { 71 try { 72 return await fs.close(file); 73 } catch (error) { 74 Logger.i(FsUtil.TAG, 'fs close error = ' + JSON.stringify(error)); 75 return error; 76 } 77 } 78 79 public static closeSync(file: number | fs.File): void | BusinessError { 80 try { 81 return fs.closeSync(file); 82 } catch (error) { 83 Logger.i(FsUtil.TAG, 'fs closeSync error = ' + JSON.stringify(error)); 84 return error; 85 } 86 } 87 88 public static async mkdir(path: string, recursion: boolean = false): Promise<void | BusinessError> { 89 try { 90 return await fs.mkdir(path, recursion); 91 } catch (error) { 92 Logger.i(FsUtil.TAG, 'fs mkdir error = ' + JSON.stringify(error)); 93 return error; 94 } 95 } 96 97 public static mkdirSync(path: string, recursion: boolean = false): void | BusinessError { 98 try { 99 return fs.mkdirSync(path, recursion); 100 } catch (error) { 101 Logger.i(FsUtil.TAG, 'fs mkdirSync error = ' + JSON.stringify(error)); 102 return error; 103 } 104 } 105 106 public static async rmdir(path: string): Promise<void | BusinessError> { 107 try { 108 return await fs.rmdir(path); 109 } catch (error) { 110 Logger.i(FsUtil.TAG, 'fs rmdir error = ' + JSON.stringify(error)); 111 return error; 112 } 113 } 114 115 public static rmdirSync(path: string): void | BusinessError { 116 try { 117 return fs.rmdirSync(path); 118 } catch (error) { 119 Logger.i(FsUtil.TAG, 'fs rmdirSync error = ' + JSON.stringify(error)); 120 return error; 121 } 122 } 123 124 public static async moveFile(src: string, dest: string, mode?: number): Promise<void | BusinessError> { 125 try { 126 return await fs.moveFile(src, dest, mode); 127 } catch (error) { 128 Logger.i(FsUtil.TAG, 'fs moveFile error = ' + JSON.stringify(error)); 129 return error; 130 } 131 } 132 133 public static async moveDir(src: string, dest: string, mode?: number): Promise<void | BusinessError> { 134 try { 135 return await fs.moveDir(src, dest, mode); 136 } catch (error) { 137 Logger.i(FsUtil.TAG, 'fs moveDir error = ' + JSON.stringify(error)); 138 return error; 139 } 140 } 141 142 public static moveFileSync(src: string, dest: string, mode?: number): void | BusinessError { 143 try { 144 return fs.moveFileSync(src, dest, mode); 145 } catch (error) { 146 Logger.i(FsUtil.TAG, 'fs moveFileSync error = ' + JSON.stringify(error)); 147 return error; 148 } 149 } 150 151 public static moveDirSync(src: string, dest: string, mode?: number): void | BusinessError { 152 try { 153 return fs.moveDirSync(src, dest, mode); 154 } catch (error) { 155 Logger.i(FsUtil.TAG, 'fs moveDirSync error = ' + JSON.stringify(error)); 156 return error; 157 } 158 } 159 160 public static async rename(oldPath: string, newPath: string): Promise<void | BusinessError> { 161 try { 162 return await fs.rename(oldPath, newPath); 163 } catch (error) { 164 Logger.i(FsUtil.TAG, 'fs rename error = ' + JSON.stringify(error)); 165 return error; 166 } 167 } 168 169 public static renameSync(oldPath: string, newPath: string): void | BusinessError { 170 try { 171 return fs.renameSync(oldPath, newPath); 172 } catch (error) { 173 Logger.i(FsUtil.TAG, 'fs renameSync error = ' + JSON.stringify(error)); 174 return error; 175 } 176 } 177 178 public static async unlink(path: string): Promise<void | BusinessError> { 179 try { 180 return await fs.unlink(path); 181 } catch (error) { 182 Logger.i(FsUtil.TAG, 'fs unlink error = ' + JSON.stringify(error)); 183 return error; 184 } 185 } 186 187 public static unlinkSync(path: string): void | BusinessError { 188 try { 189 return fs.unlinkSync(path); 190 } catch (error) { 191 Logger.i(FsUtil.TAG, 'fs unlinkSync error = ' + JSON.stringify(error)); 192 return error; 193 } 194 } 195 196 // @ts-ignore 197 public static async write(fd: number, buffer: ArrayBuffer | string, options?: fs.WriteOptions): Promise<number | BusinessError> { 198 try { 199 return await fs.write(fd, buffer, options); 200 } catch (error) { 201 Logger.i(FsUtil.TAG, 'fs write error = ' + JSON.stringify(error)); 202 return error; 203 } 204 } 205 206 // @ts-ignore 207 public static writeSync(fd: number, buffer: ArrayBuffer | string, options?: fs.WriteOptions): number | BusinessError { 208 try { 209 return fs.writeSync(fd, buffer, options); 210 } catch (error) { 211 Logger.i(FsUtil.TAG, 'fs writeSync error = ' + JSON.stringify(error)); 212 return error; 213 } 214 } 215 216 // @ts-ignore 217 public static async read(fd: number, buffer: ArrayBuffer, options?: fs.ReadOptions): Promise<number | BusinessError> { 218 try { 219 return await fs.read(fd, buffer, options); 220 } catch (error) { 221 Logger.i(FsUtil.TAG, 'fs read error = ' + JSON.stringify(error)); 222 return error; 223 } 224 } 225 226 // @ts-ignore 227 public static readSync(fd: number, buffer: ArrayBuffer, options?: fs.ReadOptions): number | BusinessError { 228 try { 229 return fs.readSync(fd, buffer, options); 230 } catch (error) { 231 Logger.i(FsUtil.TAG, 'fs readSync error = ' + JSON.stringify(error)); 232 return error; 233 } 234 } 235 236 public static readTextSync(path: string): string | BusinessError { 237 try { 238 return fs.readTextSync(path); 239 } catch (error) { 240 Logger.i(FsUtil.TAG, `fs readTextSync error = ${JSON.stringify(error)}`); 241 return error; 242 } 243 } 244 245 // @ts-ignore 246 public static listFileSync(path: string, options?: fs.ListFileOptions): string[] | BusinessError { 247 try { 248 let res = fs.listFileSync(path, options); 249 return res; 250 } catch (error) { 251 Logger.i(FsUtil.TAG, 'fs listFileSync error = ' + JSON.stringify(error)); 252 return error; 253 } 254 } 255 256 public static async fsync(fd: number): Promise<void | BusinessError> { 257 try { 258 return await fs.fsync(fd); 259 } catch (error) { 260 Logger.i(FsUtil.TAG, 'fs fsync error = ' + JSON.stringify(error)); 261 return error; 262 } 263 } 264 265 public static fsyncSync(fd: number): void | BusinessError { 266 try { 267 return fs.fsyncSync(fd); 268 } catch (error) { 269 Logger.i(FsUtil.TAG, 'fs fsync error = ' + JSON.stringify(error)); 270 return error; 271 } 272 } 273 274 /** 275 * 强制删除文件 276 * @param uri 删除文件的uri 277 */ 278 public static forceDelete(uri: string): number | BusinessError { 279 try { 280 let fileUri: fileuri.FileUri = new fileuri.FileUri(uri); 281 let filePath: string = fileUri.path; 282 if (fs.statSync(filePath).isDirectory()) { 283 fs.rmdirSync(filePath); 284 } else { 285 fs.unlinkSync(filePath); 286 } 287 return 0; 288 } catch (error) { 289 Logger.i(FsUtil.TAG, 'force delete file error: ' + JSON.stringify(error)); 290 return error; 291 } 292 } 293 294 /** 295 * 文件夹判空 296 * @param path 文件夹的uri 297 */ 298 public static isFolderEmpty(path: string): boolean | BusinessError { 299 try { 300 let fileList = fs.listFileSync(path, { listNum: 1 }); 301 return fileList.length === 0; 302 } catch (error) { 303 Logger.i(FsUtil.TAG, 'isFolderEmpty error: ' + JSON.stringify(error)); 304 return error; 305 } 306 } 307 /** 308 * 判断文件是否为文件夹(目前暂不支持应用沙箱目录) 309 * @param path 文件 310 * @returns 311 */ 312 public static isFolder(path: string): boolean | BusinessError { 313 try { 314 let stat = fs.statSync(path); 315 return stat?.isDirectory(); 316 } catch (error) { 317 Logger.i(FsUtil.TAG, path + ' isFolder error: ' + JSON.stringify(error)); 318 return error; 319 } 320 } 321 322 /** 323 * 判断文件是否存在 324 * @param uri 文件uri 325 * @returns 判断结果 326 */ 327 public static isFileExist(uri: string): boolean { 328 let isExist: boolean = false; 329 try { 330 Logger.i(FsUtil.TAG, 'open start'); 331 let fileFd: fs.File = fs.openSync(uri, fs.OpenMode.READ_ONLY); 332 fs.closeSync(fileFd); 333 isExist = true; 334 } catch (error) { 335 Logger.i(FsUtil.TAG, 'openSync fail: ' + JSON.stringify(error)); 336 } 337 return isExist; 338 } 339 340 /** 341 * 判断文件是否被删除(包括软删除和硬删除) 342 * @param uri 文件uri 343 * @returns 判断结果 344 */ 345 public static isFileDeleted(uri: string): boolean { 346 try { 347 Logger.i(FsUtil.TAG, 'open start'); 348 let fileFd: fs.File = fs.openSync(uri, fs.OpenMode.READ_ONLY); // 此处报错说明被硬删除了 349 const path = fileFd.path; 350 fs.closeSync(fileFd); 351 let stat = fs.statSync(path); 352 if (stat.ctime === 0 && stat.mtime === 0) { // 说明被软删除了 353 return true; 354 } 355 return false; 356 } catch (error) { 357 Logger.i(FsUtil.TAG, 'openSync fail: ' + JSON.stringify(error)); 358 return true; 359 } 360 } 361 362 /** 363 * 判断目录下是否存在同名文件 364 * @param destUri:目录uri 365 * @param fileName:待判断的文件名 366 * @returns 判断结果 367 */ 368 public static isExistDupName(destUri: string, fileName: string): boolean { 369 let isExistDupName: boolean = false; 370 try { 371 let destFileInfo: uri.URI = new fileuri.FileUri(destUri); 372 let newFilePath: string = destFileInfo.path + '/' + fileName; 373 isExistDupName = fs.accessSync(newFilePath); 374 } catch (err) { 375 Logger.i(FsUtil.TAG, 'isExistDupName err: ' + JSON.stringify(err)); 376 } 377 return isExistDupName; 378 } 379 380 public static getInoByUri(uri: string): string { 381 try { 382 let fileUri: fileuri.FileUri = new fileuri.FileUri(uri); 383 let stat = fs.statSync(fileUri.path); 384 return stat.ino.toString(); 385 } catch (error) { 386 Logger.i(FsUtil.TAG, `get ino failed, error message : ${error?.message}, error code : ${error?.code}`); 387 return ''; 388 } 389 } 390 391 /** 392 * 文件拷贝同步接口,适合十几兆的小文件拷贝 393 * @param srcPath 源文件 394 * @param destinationPath 目标文件 395 * @param mode 拷贝模式 396 * @returns true:拷贝成功,目标文件已经存在 397 */ 398 public static copyFileSyncByPath(srcPath: string, destinationPath: string, mode?: number): boolean { 399 try { 400 fs.copyFileSync(srcPath, destinationPath, mode); 401 return FsUtil.isExistSyncByPath(destinationPath); 402 } catch (error) { 403 Logger.i(FsUtil.TAG, 'copyFileSyncByPath err: ' + JSON.stringify(error)); 404 return false; 405 } 406 } 407 408 /** 409 * 判断文件是否存在 410 * 411 * @param path 文件全目录 412 * @returns true:文件存在 413 */ 414 public static isExistSyncByPath(path: string): boolean { 415 try { 416 return fs.accessSync(path); 417 } catch (error) { 418 Logger.i(FsUtil.TAG, 'isExistSyncByPath error = ' + JSON.stringify(error)); 419 return false; 420 } 421 } 422 423 /** 424 * 重命名异步接口 425 * @param oldPath 即将要重命名的文件全路径 426 * @param newPath 重命名之后的文件全路径 427 * @returns true:命名成功 428 */ 429 public static renameSyncByPath(oldPath: string, newPath: string): boolean { 430 try { 431 fs.renameSync(oldPath, newPath); 432 return FsUtil.isExistSyncByPath(newPath); 433 } catch (error) { 434 Logger.i(FsUtil.TAG, 'fs renameSync error = ' + JSON.stringify(error)); 435 return false; 436 } 437 } 438 439 /** 440 * 同步获取文件大小 441 * @param path 文件全路径 442 * @returns 文件夹大小 443 */ 444 public static getFileSizeSyncByPath(path: string): number { 445 let size = 0; 446 try { 447 let stat = fs.statSync(path); 448 size = stat.size; 449 } catch (error) { 450 Logger.i(FsUtil.TAG, 'getFileInfoByFs fail, error:' + JSON.stringify(error)); 451 } 452 return size; 453 } 454}