HarmonyOS 鸿蒙Next中实现文件读写与本地存储管理
HarmonyOS 鸿蒙Next中实现文件读写与本地存储管理 在鸿蒙os开发中如何实现文件读写怀本地存储管理呢?
3 回复
应用场景
在应用中,经常需要对用户生成的内容进行临时保存,如 编辑器草稿自动保存、表单填写中途退出后恢复。有时也需要针对用户的媒体资源进行缓存,如下载图片/音频到本地,避免重复请求,再如离线查看已加载的文档,所有需要进行缓存的文件就需要进行文件的操作及存储。
实现思路
鸿蒙os已经为我们提供了大量的操作文件句柄的类了,但还是需要我们进行一些封装使用,比如封装一些获取文件路径、上传、下载等各种方法。
核心的用法包括:@ohos.file.fs 、 @ohos.file.fileuri 这2个文件操作的类。
完整代码
文件操作类
import fileUri from '[@ohos](/user/ohos).file.fileuri';
import fs, { ListFileOptions, ReadOptions, ReadTextOptions, WriteOptions } from '[@ohos](/user/ohos).file.fs';
import { StrUtil } from './StrUtil';
import { fileShare } from '[@kit](/user/kit).CoreFileKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
export class FileUtil {
static readonly separator: string = '/'; // 文件路径分隔符
/**
* 获取文件目录下的文件夹路径或文件路径。
*
* [@param](/user/param) dirPath 文件路径;支持完整路径和相对路径(如 `download/wps/doc`);传空字符串表示根目录。
* [@param](/user/param) fileName 文件名(如 `test.text`);传空字符串表示文件夹路径。
* [@param](/user/param) blHap 是否为 HAP 级别文件路径:
* - `true`:HAP 级别文件路径。
* - `false`:App 级别文件路径。
* [@returns](/user/returns) 返回完整的文件路径。
*/
static getFilesDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string {
let filePath = blHap ? getContext().filesDir : getContext().getApplicationContext().filesDir; // 根目录
return FileUtil.buildFilePath(filePath, dirPath, fileName);
}
/**
* 获取缓存目录下的文件夹路径或文件路径。
*
* [@param](/user/param) dirPath 文件路径;支持完整路径和相对路径(如 `download/wps/doc`);传空字符串表示根目录。
* [@param](/user/param) fileName 文件名(如 `test.text`);传空字符串表示文件夹路径。
* [@param](/user/param) blHap 是否为 HAP 级别文件路径:
* - `true`:HAP 级别文件路径。
* - `false`:App 级别文件路径。
* [@returns](/user/returns) 返回完整的文件路径。
*/
static getCacheDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string {
let filePath = blHap ? getContext().cacheDir : getContext().getApplicationContext().cacheDir; // 根目录
return FileUtil.buildFilePath(filePath, dirPath, fileName);
}
/**
* 获取临时目录下的文件夹路径或文件路径。
*
* [@param](/user/param) dirPath 文件路径;支持完整路径和相对路径(如 `download/wps/doc`);传空字符串表示根目录。
* [@param](/user/param) fileName 文件名(如 `test.text`);传空字符串表示文件夹路径。
* [@param](/user/param) blHap 是否为 HAP 级别文件路径:
* - `true`:HAP 级别文件路径。
* - `false`:App 级别文件路径。
* [@returns](/user/returns) 返回完整的文件路径。
*/
static getTempDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string {
let filePath = blHap ? getContext().tempDir : getContext().getApplicationContext().tempDir; // 根目录
return FileUtil.buildFilePath(filePath, dirPath, fileName);
}
/**
* 构建文件路径的通用逻辑。
*
* [@param](/user/param) rootPath 根目录路径。
* [@param](/user/param) dirPath 子目录路径。
* [@param](/user/param) fileName 文件名。
* [@returns](/user/returns) 返回完整的文件路径。
*/
private static buildFilePath(rootPath: string, dirPath: string, fileName: string): string {
let filePath = rootPath;
if (StrUtil.isNotEmpty(dirPath)) {
if (FileUtil.hasDirPath(dirPath)) { // 路径中包含根目录,是完整路径。
filePath = dirPath;
} else { // 路径中不包含根目录,拼接成完整路径。
filePath = `${filePath}${FileUtil.separator}${dirPath}`;
}
if (!FileUtil.accessSync(filePath)) {
FileUtil.mkdirSync(filePath); // 如果文件夹不存在就创建。
}
}
if (StrUtil.isNotEmpty(fileName)) {
filePath = `${filePath}${FileUtil.separator}${fileName}`;
}
return filePath;
}
/**
* 判断是否是完整路径。
*
* [@param](/user/param) path 文件路径。
* [@returns](/user/returns) 返回布尔值,表示是否是完整路径。
*/
static hasDirPath(path: string): boolean {
return path.startsWith("/data/storage/") || path.startsWith("/storage/");
}
/**
* 通过 URI 或路径,获取 FileUri 对象。
*
* [@param](/user/param) uriOrPath URI 或路径。
* [@returns](/user/returns) 返回 FileUri 对象。
*/
static getFileUri(uriOrPath: string): fileUri.FileUri {
return new fileUri.FileUri(uriOrPath);
}
/**
* 通过 URI 或路径,获取文件名。
*
* [@param](/user/param) uriOrPath URI 或路径。
* [@returns](/user/returns) 返回文件名。
*/
static getFileName(uriOrPath: string): string {
return FileUtil.getFileUri(uriOrPath).name;
}
/**
* 通过 URI 或路径,获取文件路径。
*
* [@param](/user/param) uriOrPath URI 或路径。
* [@returns](/user/returns) 返回文件路径。
*/
static getFilePath(uriOrPath: string): string {
return FileUtil.getFileUri(uriOrPath).path;
}
/**
* 通过 URI 或路径,获取对应文件父目录的 URI。
*
* [@param](/user/param) uriOrPath URI 或路径。
* [@returns](/user/returns) 返回父目录的 URI。
*/
static getParentUri(uriOrPath: string): string {
return FileUtil.getFileUri(uriOrPath).getFullDirectoryUri();
}
/**
* 通过 URI 或路径,获取对应文件父目录的路径名。
*
* [@param](/user/param) uriOrPath URI 或路径。
* [@returns](/user/returns) 返回父目录的路径名。
*/
static getParentPath(uriOrPath: string): string {
const parentUri = FileUtil.getParentUri(uriOrPath);
return FileUtil.getFilePath(parentUri);
}
/**
* 以同步方法获取文件 URI。
*
* [@param](/user/param) path 应用沙箱路径。
* [@returns](/user/returns) 返回文件 URI。
*/
static getUriFromPath(path: string): string {
return fileUri.getUriFromPath(path);
}
/**
* 根据文件名获取文件后缀。
*
* [@param](/user/param) fileName 文件名(如 `test.txt` 或 `test.doc`)。
* [@returns](/user/returns) 返回文件后缀(如 `txt` 或 `doc`)。
*/
static getFileExtension(fileName: string): string {
if (StrUtil.isNotEmpty(fileName) && fileName.includes(".")) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
return '';
}
/**
* 获取指定文件夹下所有文件的大小或指定文件的大小。
*
* [@param](/user/param) path 文件夹路径或文件路径。
* [@returns](/user/returns) 返回文件或文件夹的总大小(单位:字节)。
*/
static getFileDirSize(path: string): number {
if (!FileUtil.accessSync(path)) {
return 0; // 路径不存在时返回 0。
}
if (FileUtil.isDirectory(path)) { // 文件夹
let totalSize = 0;
FileUtil.listFileSync(path, { recursion: true }).forEach((filePath) => {
try {
totalSize += FileUtil.statSync(filePath).size;
} catch (error) {
console.warn(`Failed to get size for file ${filePath}. Error: ${error.message}`);
}
});
return totalSize;
} else { // 文件
return FileUtil.statSync(path).size;
}
}
/**
* 判断文件是否是普通文件。
*
* [@param](/user/param) file 文件的应用沙箱路径或已打开的文件描述符。
* [@returns](/user/returns) 返回布尔值,表示是否是普通文件。
*/
static isFile(file: string | number): boolean {
try {
return fs.statSync(file).isFile();
} catch (error) {
throw new Error(`Failed to check if file is a regular file. Path/FD: ${file}. Error: ${error.message}`);
}
}
/**
* 判断文件是否是目录。
*
* [@param](/user/param) file 文件的应用沙箱路径或已打开的文件描述符。
* [@returns](/user/returns) 返回布尔值,表示是否是目录。
*/
static isDirectory(file: string | number): boolean {
try {
return fs.statSync(file).isDirectory();
} catch (error) {
throw new Error(`Failed to check if file is a directory. Path/FD: ${file}. Error: ${error.message}`);
}
}
/**
* 重命名文件或文件夹,使用 Promise 异步回调。
*
* [@param](/user/param) oldPath 文件的应用沙箱原路径。
* [@param](/user/param) newPath 文件的应用沙箱新路径。
* [@returns](/user/returns) 返回一个无返回值的 Promise。
*/
static rename(oldPath: string, newPath: string): Promise<void> {
return fs.rename(oldPath, newPath).catch((error:BusinessError) => {
throw new Error(`Failed to rename file/directory from ${oldPath} to ${newPath}. Error: ${error.message}`);
});
}
/**
* 重命名文件或文件夹,以同步方法。
*
* [@param](/user/param) oldPath 文件的应用沙箱原路径。
* [@param](/user/param) newPath 文件的应用沙箱新路径。
*/
static renameSync(oldPath: string, newPath: string): void {
try {
fs.renameSync(oldPath, newPath);
} catch (error) {
throw new Error(`Failed to rename file/directory from ${oldPath} to ${newPath}. Error: ${error.message}`);
}
}
/**
* 创建目录,支持多层级创建。
*
* [@param](/user/param) path 目录的应用沙箱路径。
* [@param](/user/param) recursion 是否多层级创建目录:
* - `true`:递归创建多级目录。
* - `false`:仅创建单层目录。
* [@returns](/user/returns) 返回一个无返回值的 Promise。
*/
static mkdir(path: string, recursion: boolean = true): Promise<void> {
return fs.mkdir(path, recursion).catch((error:BusinessError) => {
throw new Error(`Failed to create directory at ${path}. Recursion: ${recursion}. Error: ${error.message}`);
});
}
/**
* 创建目录,以同步方法,支持多层级创建。
*
* [@param](/user/param) path 目录的应用沙箱路径。
* [@param](/user/param) recursion 是否多层级创建目录:
* - `true`:递归创建多级目录。
* - `false`:仅创建单层目录。
*/
static mkdirSync(path: string, recursion: boolean = true): void {
try {
fs.mkdirSync(path, recursion);
} catch (error) {
throw new Error(`Failed to create directory at ${path}. Recursion: ${recursion}. Error: ${error.message}`);
}
}
/**
* 删除整个目录。
*
* [@param](/user/param) path 目录的应用沙箱路径。
* [@returns](/user/returns) 返回一个无返回值的 Promise。
*/
static rmdir(path: string): Promise<void> {
return fs.rmdir(path).catch((error:BusinessError) => {
throw new Error(`Failed to remove directory at ${path}. Error: ${error.message}`);
});
}
/**
* 删除整个目录,以同步方法。
*
* [@param](/user/param) path 目录的应用沙箱路径。
*/
static rmdirSync(path: string): void {
try {
fs.rmdirSync(path);
} catch (error) {
throw new Error(`Failed to remove directory at ${path}. Error: ${error.message}`);
}
}
/**
* 删除单个文件。
*
* [@param](/user/param) path 文件的应用沙箱路径。
* [@returns](/user/returns) 返回一个无返回值的 Promise。
*/
static unlink(path: string): Promise<void> {
return fs.unlink(path).catch((error:BusinessError) => {
throw new Error(`Failed to delete file at ${path}. Error: ${error.message}`);
});
}
/**
* 删除单个文件,以同步方法。
*
* [@param](/user/param) path 文件的应用沙箱路径。
*/
static unlinkSync(path: string): void {
try {
fs.unlinkSync(path);
} catch (error) {
throw new Error(`Failed to delete file at ${path}. Error: ${error.message}`);
}
}
/**
* 检查文件是否存在。
*
* [@param](/user/param) path 文件应用沙箱路径。
* [@returns](/user/returns) 返回一个布尔值的 Promise。
*/
static access(path: string): Promise<boolean> {
return fs.access(path).catch((error:BusinessError) => {
throw new Error(`Failed to check file existence at ${path}. Error: ${error.message}`);
});
}
/**
* 检查文件是否存在,以同步方法。
*
* [@param](/user/param) path 文件应用沙箱路径。
* [@returns](/user/returns) 返回布尔值,表示文件是否存在。
*/
static accessSync(path: string): boolean {
try {
return fs.accessSync(path);
} catch (error) {
throw new Error(`Failed to check file existence at ${path}. Error: ${error.message}`);
}
}
/**
* 打开文件,支持使用 URI 打开。
*
* [@param](/user/param) path 文件的应用沙箱路径或 URI。
* [@param](/user/param) mode 打开文件的选项,默认为只读方式打开。
* [@returns](/user/returns) 返回文件对象的 Promise。
*/
static open(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): Promise<fs.File> {
return fs.open(path, mode).catch((error:BusinessError) => {
throw new Error(`Failed to open file at ${path}. Mode: ${mode}. Error: ${error.message}`);
});
}
/**
* 打开文件,支持使用 URI 打开,以同步方法。
*
* [@param](/user/param) path 文件的应用沙箱路径或 URI。
* [@param](/user/param) mode 打开文件的选项,默认为只读方式打开。
* [@returns](/user/returns) 返回文件对象。
*/
static openSync(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): fs.File {
try {
return fs.openSync(path, mode);
} catch (error) {
throw new Error(`Failed to open file at ${path}. Mode: ${mode}. Error: ${error.message}`);
}
}
/**
* 从文件读取数据。
*
* [@param](/user/param) fd 已打开的文件描述符。
* [@param](/user/param) buffer 用于保存读取到的文件数据的缓冲区。
* [@param](/user/param) options 可选参数:
* - offset:读取文件的位置。
* - length:读取数据的长度。
* [@returns](/user/returns) 返回实际读取的数据长度的 Promise。
*/
static read(fd: number, buffer: ArrayBuffer, options?: ReadOptions): Promise<number> {
return fs.read(fd, buffer, options).catch((error:BusinessError) => {
throw new Error(`Failed to read file data. FD: ${fd}. Error: ${error.message}`);
});
}
/**
* 从文件读取数据,以同步方法。
*
* [@param](/user/param) fd 已打开的文件描述符。
* [@param](/user/param) buffer 用于保存读取到的文件数据的缓冲区。
* [@param](/user/param) options 可选参数:
* - offset:读取文件的位置。
* - length:读取数据的长度。
* [@returns](/user/returns) 返回实际读取的数据长度。
*/
static readSync(fd: number, buffer: ArrayBuffer, options?: ReadOptions): number {
try {
return fs.readSync(fd, buffer, options);
} catch (error) {
throw new Error(`Failed to read file data. FD: ${fd}. Error: ${error.message}`);
}
}
/**
* 基于文本方式读取文件内容。
*
* [@param](/user/param) filePath 文件的应用沙箱路径。
* [@param](/user/param) options 可选参数:
* - offset:读取文件的位置。
* - length:读取数据的长度。
* - encoding:编码方式,默认为 'utf-8'。
* [@returns](/user/returns) 返回文件内容的 Promise。
*/
static readText(filePath: string, options?: ReadTextOptions): Promise<string> {
return fs.readText(filePath, options).catch((error:BusinessError) => {
throw new Error(`Failed to read text file at ${filePath}. Error: ${error.message}`);
});
}
/**
* 基于文本方式读取文件内容,以同步方法。
*
* [@param](/user/param) filePath 文件的应用沙箱路径。
* [@param](/user/param) options 可选参数:
* - offset:读取文件的位置。
* - length:读取数据的长度。
* - encoding:编码方式,默认为 'utf-8'。
* [@returns](/user/returns) 返回文件内容。
*/
static readTextSync(filePath: string, options?: ReadTextOptions): string {
try {
return fs.readTextSync(filePath, options);
} catch (error) {
throw new Error(`Failed to read text file at ${filePath}. Error: ${error.message}`);
}
}
/**
* 将数据写入文件。
*
* [@param](/user/param) fd 已打开的文件描述符。
* [@param](/user/param) buffer 待写入文件的数据(可以是缓冲区或字符串)。
* [@param](/user/param) options 可选参数更多关于HarmonyOS 鸿蒙Next中实现文件读写与本地存储管理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS Next中文件读写通过FileManager和FileIO模块实现。使用FileManager进行文件系统操作,如创建、删除、移动文件或目录。FileIO模块提供文件流读写功能,支持同步和异步操作。本地存储管理通过Preferences或分布式数据对象实现轻量级数据持久化,适用于键值对存储。安全沙箱机制确保应用数据隔离,访问外部存储需申请相应权限。开发者需在module.json5中声明所需存储权限。


