HarmonyOS 鸿蒙Next开发者技术支持-文件操作指南
HarmonyOS 鸿蒙Next开发者技术支持-文件操作指南
问题场景
在鸿蒙应用开发中,开发者经常需要对文件系统进行各种操作,包括但不限于:
- 创建、删除、重命名文件夹
- 遍历文件夹内容
- 查询文件夹属性信息
- 跨应用文件夹访问
- 管理应用沙箱内外部文件夹
具体表现
- API分散不统一:文件夹相关API分布在多个模块中(@ohos.file.fs, @ohos.file.fileuri等)
- 权限配置复杂:不同文件夹操作需要不同的权限声明
- 路径处理混乱:沙箱路径、公共路径、外部路径混合使用容易出错
- 异步操作回调嵌套:深层次的回调嵌套导致代码难以维护
- 兼容性问题:不同设备、不同版本的API差异
优化方向
- 统一封装:提供简洁一致的API接口
- 路径标准化:统一处理各种路径格式
- 权限管理:简化权限申请和检查逻辑
- 错误处理:统一错误码转换和异常抛出
- 异步优化:提供Promise和async/await支持
方案一:创建文件夹操作工具类
// FileDirectoryManager.ts
import fs from '[@ohos](/user/ohos).file.fs';
import fileUri from '[@ohos](/user/ohos).file.fileuri';
import common from '[@ohos](/user/ohos).app.ability.common';
/**
* 鸿蒙文件夹操作管理器
*/
export class FileDirectoryManager {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
/**
* 创建文件夹
* @param dirPath 文件夹路径
* @param recursive 是否递归创建父目录
*/
async createDirectory(dirPath: string, recursive: boolean = true): Promise<void> {
try {
// 标准化路径
const normalizedPath = this.normalizePath(dirPath);
// 检查文件夹是否已存在
const isExist = await this.checkDirectoryExists(normalizedPath);
if (isExist) {
console.info(`Directory already exists: ${normalizedPath}`);
return;
}
// 创建文件夹
await fs.mkdir(normalizedPath, recursive);
console.info(`Directory created successfully: ${normalizedPath}`);
} catch (error) {
console.error(`Failed to create directory: ${dirPath}`, error);
throw this.wrapFileError(error, 'createDirectory');
}
}
/**
* 删除文件夹
* @param dirPath 文件夹路径
* @param recursive 是否递归删除
*/
async deleteDirectory(dirPath: string, recursive: boolean = true): Promise<void> {
try {
const normalizedPath = this.normalizePath(dirPath);
await fs.rmdir(normalizedPath, recursive);
console.info(`Directory deleted successfully: ${normalizedPath}`);
} catch (error) {
console.error(`Failed to delete directory: ${dirPath}`, error);
throw this.wrapFileError(error, 'deleteDirectory');
}
}
/**
* 重命名文件夹
* @param oldPath 原路径
* @param newPath 新路径
*/
async renameDirectory(oldPath: string, newPath: string): Promise<void> {
try {
const normalizedOldPath = this.normalizePath(oldPath);
const normalizedNewPath = this.normalizePath(newPath);
await fs.rename(normalizedOldPath, normalizedNewPath);
console.info(`Directory renamed from ${oldPath} to ${newPath}`);
} catch (error) {
console.error(`Failed to rename directory: ${oldPath} -> ${newPath}`, error);
throw this.wrapFileError(error, 'renameDirectory');
}
}
/**
* 列出文件夹内容
* @param dirPath 文件夹路径
*/
async listDirectory(dirPath: string): Promise<string[]> {
try {
const normalizedPath = this.normalizePath(dirPath);
const dir = await fs.opendir(normalizedPath);
const files: string[] = [];
let isDone = false;
while (!isDone) {
const result = await dir.read();
if (result && result.name) {
files.push(result.name);
} else {
isDone = true;
}
}
await dir.close();
return files;
} catch (error) {
console.error(`Failed to list directory: ${dirPath}`, error);
throw this.wrapFileError(error, 'listDirectory');
}
}
/**
* 获取文件夹信息
* @param dirPath 文件夹路径
*/
async getDirectoryInfo(dirPath: string): Promise<fs.FileInfo> {
try {
const normalizedPath = this.normalizePath(dirPath);
const stat = await fs.stat(normalizedPath);
return stat;
} catch (error) {
console.error(`Failed to get directory info: ${dirPath}`, error);
throw this.wrapFileError(error, 'getDirectoryInfo');
}
}
/**
* 检查文件夹是否存在
*/
async checkDirectoryExists(dirPath: string): Promise<boolean> {
try {
const normalizedPath = this.normalizePath(dirPath);
await fs.access(normalizedPath);
return true;
} catch {
return false;
}
}
/**
* 复制文件夹
* @param sourcePath 源路径
* @param targetPath 目标路径
*/
async copyDirectory(sourcePath: string, targetPath: string): Promise<void> {
try {
const normalizedSource = this.normalizePath(sourcePath);
const normalizedTarget = this.normalizePath(targetPath);
// 创建目标文件夹
await this.createDirectory(normalizedTarget);
// 获取源文件夹内容
const files = await this.listDirectory(normalizedSource);
// 复制每个文件/子文件夹
for (const file of files) {
const sourceFile = `${normalizedSource}/${file}`;
const targetFile = `${normalizedTarget}/${file}`;
const stat = await fs.stat(sourceFile);
if (stat.isDirectory()) {
// 递归复制子文件夹
await this.copyDirectory(sourceFile, targetFile);
} else {
// 复制文件
await fs.copyFile(sourceFile, targetFile);
}
}
} catch (error) {
console.error(`Failed to copy directory: ${sourcePath} -> ${targetPath}`, error);
throw this.wrapFileError(error, 'copyDirectory');
}
}
/**
* 获取应用沙箱目录
*/
getSandboxDir(type: 'files' | 'cache' | 'temp' | 'preferences' = 'files'): string {
const dirs = this.context.filesDir;
switch (type) {
case 'cache':
return this.context.cacheDir;
case 'temp':
return this.context.tempDir;
case 'preferences':
return this.context.preferencesDir;
case 'files':
default:
return dirs;
}
}
/**
* 标准化路径
*/
private normalizePath(path: string): string {
// 处理相对路径
if (path.startsWith('./') || path.startsWith('../')) {
return this.getSandboxDir('files') + '/' + path;
}
// 处理沙箱路径简写
if (path.startsWith('sandbox://')) {
const relativePath = path.replace('sandbox://', '');
return this.getSandboxDir('files') + '/' + relativePath;
}
return path;
}
/**
* 包装文件错误
*/
private wrapFileError(error: any, operation: string): Error {
const errorCode = error.code || -1;
const errorMessage = this.getErrorMessage(errorCode, operation);
return new Error(`${operation} failed: ${errorMessage} (Code: ${errorCode})`);
}
/**
* 获取错误信息
*/
private getErrorMessage(code: number, operation: string): string {
const errorMap: Record<number, string> = {
13900001: '参数检查失败',
13900002: '路径超出最大长度限制',
13900003: '路径中不允许出现特殊字符',
13900004: '文件或目录不存在',
13900005: '没有访问权限',
13900006: '文件或目录已存在',
13900007: '磁盘空间不足',
13900008: '输入输出错误',
13900009: '网络错误',
13900010: '不支持的操作',
};
return errorMap[code] || `未知错误,操作: ${operation}`;
}
}
方案二:权限配置模板
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.READ_MEDIA",
"reason": "需要读取媒体文件",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "需要保存文件到媒体目录",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.MEDIA_LOCATION",
"reason": "需要访问媒体文件的位置信息",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
方案三:使用示例
// 使用示例
import { FileDirectoryManager } from './FileDirectoryManager';
import common from '[@ohos](/user/ohos).app.ability.common';
class DirectoryExample {
private fileManager: FileDirectoryManager;
constructor(context: common.UIAbilityContext) {
this.fileManager = new FileDirectoryManager(context);
}
// 示例1:创建应用数据文件夹
async setupAppDirectories() {
try {
// 创建主数据目录
await this.fileManager.createDirectory('data');
// 创建子目录
await this.fileManager.createDirectory('data/images');
await this.fileManager.createDirectory('data/documents');
await this.fileManager.createDirectory('data/cache');
console.info('App directories created successfully');
} catch (error) {
console.error('Failed to setup app directories', error);
}
}
// 示例2:清理缓存文件夹
async clearCache() {
try {
const cacheDir = this.fileManager.getSandboxDir('cache');
const files = await this.fileManager.listDirectory(cacheDir);
for (const file of files) {
const filePath = `${cacheDir}/${file}`;
const stat = await this.fileManager.getDirectoryInfo(filePath);
if (stat.isDirectory()) {
await this.fileManager.deleteDirectory(filePath);
} else {
// 如果是文件,使用fs.unlink删除
// 这里可以扩展FileDirectoryManager支持文件删除
}
}
console.info('Cache cleared successfully');
} catch (error) {
console.error('Failed to clear cache', error);
}
}
// 示例3:备份数据
async backupData() {
try {
const sourceDir = 'sandbox://data';
const backupDir = `backup_${new Date().getTime()}`;
await this.fileManager.createDirectory(backupDir);
await this.fileManager.copyDirectory(sourceDir, backupDir);
console.info(`Data backed up to: ${backupDir}`);
} catch (error) {
console.error('Failed to backup data', error);
}
}
}
方案四:路径处理工具
// PathUtils.ts
export class PathUtils {
/**
* 获取路径的目录部分
*/
static getDirectory(path: string): string {
const lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex === -1) return '.';
return path.substring(0, lastSlashIndex);
}
/**
* 获取文件名
*/
static getFileName(path: string): string {
const lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex === -1) return path;
return path.substring(lastSlashIndex + 1);
}
/**
* 获取文件扩展名
*/
static getFileExtension(path: string): string {
const fileName = this.getFileName(path);
const lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex === -1) return '';
return fileName.substring(lastDotIndex + 1);
}
/**
* 连接路径
*/
static join(...paths: string[]): string {
return paths.join('/').replace(/\/+/g, '/');
}
/**
* 检查是否是绝对路径
*/
static isAbsolutePath(path: string): boolean {
return path.startsWith('/') ||
path.startsWith('bundle://') ||
path.startsWith('internal://');
}
}
结果展示:开发效率提升或为后续同类问题提供参考
质量改善
- 统一性:所有文件夹操作使用统一接口
- 可读性:方法命名清晰,参数明确
- 可扩展性:易于添加新的文件夹操作方法
- 错误处理:统一的错误处理机制,便于问题定位
复用价值
- 跨项目使用:工具类可直接复制到其他鸿蒙项目
- 团队规范:建立团队内文件夹操作的最佳实践
- 新人上手:新开发者可快速掌握文件夹操作
- 文档补充:为官方文档提供实际使用案例参考
更多关于HarmonyOS 鸿蒙Next开发者技术支持-文件操作指南的实战教程也可以访问 https://www.itying.com/category-93-b0.html
2 回复
鸿蒙Next文件操作
鸿蒙Next文件操作使用ArkTS语言,通过@ohos.file.fs等API实现。
主要接口
fs.access:检查文件存在性fs.copyFile:复制文件fs.mkdir:创建目录fs.open:打开文件流fs.read:读取数据fs.write:写入数据fs.list:获取目录列表
使用说明
- 文件路径:需使用应用沙箱路径,通过
Context获取。 - 权限声明:操作需声明
ohos.permission.READ_USER_STORAGE或WRITE_USER_STORAGE权限。
更多关于HarmonyOS 鸿蒙Next开发者技术支持-文件操作指南的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
这个文件操作工具类封装得非常全面,针对HarmonyOS Next开发中文件API分散、路径混乱等痛点提供了很好的解决方案。
核心优势在于:
- 统一异步接口:所有方法都使用async/await,避免了回调嵌套问题。
- 路径标准化:通过normalizePath方法统一处理沙箱路径、相对路径等,简化了开发者的路径处理逻辑。
- 完整的错误处理:wrapFileError方法将系统错误码转换为可读的中文错误信息,便于调试。
- 沙箱目录管理:getSandboxDir方法提供了便捷的沙箱目录访问方式。
需要注意的几个关键点:
- 权限配置需要根据实际使用场景调整,特别是访问公共目录时需要申请相应权限。
- copyDirectory方法实现了递归复制,但需要注意大文件夹复制时的性能问题。
- 对于文件操作,可以进一步扩展该类,增加文件读写、移动等常用操作。
这个方案确实能显著提升开发效率,特别是在需要频繁操作文件系统的应用中。路径处理工具类的补充也让整个方案更加完整。

