OpenHarmony 本地文件 FS 文件系统操作封装(API Version23 + 适配版) 📅 2026/7/19 15:19:21 摘要fs 文件系统模块用于应用沙盒目录下文件 / 文件夹的创建、读写、删除、遍历常用于保存相机拍摄照片、缓存文本、本地日志、离线文档等场景。API Version23 重构文件句柄生命周期、同步 / 异步 IO、权限隔离、大文件分片读写逻辑修复低版本文件句柄泄漏、大文件读取卡顿、文件夹遍历报错、文件删除残留、沙盒路径访问失效等兼容缺陷。旧项目升级 API23 后常出现多次读写文件占用无法删除、读取超大文本页面卡死、创建文件夹失败、退出应用文件句柄未释放内存上涨等问题。本文基于 DevEco Studio 适配 OpenHarmony API23 及以上版本封装通用文件工具类实现文件读写、文件夹创建、文件删除、遍历目录、图片缓存存储五大核心能力搭配日志保存、相机图片缓存两大实战页面提供完整可运行代码输出文件句柄关闭、沙盒路径使用、大文件分片规范汇总版本升级兼容故障解决方案为鸿蒙本地文件管理开发提供标准化实操模板。关键词OpenHarmonyArkUIAPI Version23fs文件系统沙盒目录文件读写目录遍历一、引言1.1 文件系统开发背景应用所有私有文件只能存储在系统分配的独立沙盒目录无法随意访问设备公共存储。相机拍摄图片、本地日志、离线缓存、自定义文档都依赖 ohos.file.fs 完成操作。文件操作分为同步、异步两套 API同步 IO 简单但主线程大量读写会阻塞 UI异步 IO 适合大文件、批量操作。 OpenHarmony API Version23 针对 fs 底层 IO 引擎完成全面升级核心变更点强制文件 fd 句柄使用后必须 closeSync 释放长期不关闭会锁定文件统一沙盒路径上下文获取规则废弃硬编码固定路径写法优化大文件 Buffer 分片读取避免一次性加载全部内容占用内存区分文件 / 文件夹操作接口新增 existsSync 统一判断资源是否存在修复多线程并发读写同一文件内容错乱问题增加文件读写互斥逻辑限制跨沙盒、系统目录访问仅允许操作自身 context 沙盒路径。大量 API9~11 旧项目升级后文件删不掉、重复写入内容错乱、读取大文件页面卡死根源是未及时关闭文件 fd、主线程大量同步 IO因此掌握 API23 标准 fs 封装规范是本地缓存、文件存储类功能必备技能。1.2 开发环境与测试场景开发工具DevEco Studio 5.0 及以上 适配系统OpenHarmony API Version23、HarmonyOS NEXT 导入模块ohos.file.fs、ohos.app.ability.common 无需额外动态权限沙盒目录应用自带读写权限 测试场景写入本地日志文本、相机图片保存、创建缓存文件夹、遍历图片目录、删除过期缓存文件二、API23 fs 文件系统核心 API 与版本变更说明2.1 常用沙盒基础路径通过上下文获取etsconst ctx getContext(this) ctx.filesDir // 持久化文件目录常用图片、文档 ctx.cacheDir // 缓存目录系统可自动清理 ctx.tempDir // 临时目录应用重启清空2.2 核心同步基础 API轻量小文件使用fs.existsSync (path)判断文件 / 文件夹是否存在fs.mkdirSync (path)创建文件夹fs.openSync (path, mode)打开文件获取 fd 句柄fs.writeSync (fd, buffer / 字符串)写入内容fs.readSync (fd, buffer)读取内容fs.closeSync (fd)释放文件句柄必执行fs.unlinkSync (path)删除文件fs.rmdirSync (path)删除空文件夹fs.readdirSync (path)遍历目录下所有文件名称2.3 文件打开模式常量etsfs.OpenMode.CREATE // 文件不存在则创建 fs.OpenMode.WRITE_ONLY // 只写 fs.OpenMode.READ_ONLY // 只读 fs.OpenMode.READ_WRITE // 读写 fs.OpenMode.APPEND // 追加写入不覆盖原有内容2.4 API23 废弃与强制约束废弃不关闭 fd 的文件读写逻辑长期运行会出现文件占用报错禁止硬编码绝对路径全部通过 UIAbility 上下文获取沙盒根目录主线程禁止同步一次性读取 1MB 以上大文件必须分片异步读取rmdirSync 仅能删除空文件夹删除带文件目录需先遍历删除内部文件不支持直接访问外部存储、相册原始路径媒体文件统一用 picker uri。三、API23 标准基础示例代码3.1 写入文本文件追加日志etsimport fs from ohos.file.fs import common from ohos.app.ability.common function writeLog(ctx: common.UIAbilityContext, text: string) { const logPath ctx.filesDir /log.txt const fd fs.openSync(logPath, fs.OpenMode.CREATE | fs.OpenMode.APPEND | fs.OpenMode.READ_WRITE) const content text \n fs.writeSync(fd, content) fs.closeSync(fd) }3.2 读取全部文本文件etsfunction readTextFile(ctx: common.UIAbilityContext, filePath: string): string { if (!fs.existsSync(filePath)) return const fd fs.openSync(filePath, fs.OpenMode.READ_ONLY) const stat fs.statSync(filePath) const buf new ArrayBuffer(stat.size) fs.readSync(fd, buf) fs.closeSync(fd) return String.fromCharCode(...new Uint8Array(buf)) }3.3 遍历文件夹所有文件名称etsfunction listDir(ctx: common.UIAbilityContext, dirPath: string): string[] { if (!fs.existsSync(dirPath)) return [] return fs.readdirSync(dirPath) }四、两大业务完整实战案例全兼容 API234.1 实战一全局文件工具类封装utils/file_util.etsetsimport fs from ohos.file.fs import common from ohos.app.ability.common import promptAction from ohos.promptAction class FileUtil { private static instance: FileUtil private ctx: common.UIAbilityContext | null null static getInstance(): FileUtil { if (!FileUtil.instance) { FileUtil.instance new FileUtil() } return FileUtil.instance } setContext(context: common.UIAbilityContext) { this.ctx context } // 获取files持久化根目录 getFilesRoot(): string { return this.ctx?.filesDir ?? } // 获取cache缓存目录 getCacheRoot(): string { return this.ctx?.cacheDir ?? } // 判断文件/文件夹是否存在 exists(path: string): boolean { return fs.existsSync(path) } // 创建文件夹 mkdir(dirPath: string): boolean { try { if (!this.exists(dirPath)) { fs.mkdirSync(dirPath) } return true } catch (err) { promptAction.showToast({ message: 文件夹创建失败 }) return false } } // 写入文本覆盖原有内容 writeText(path: string, content: string): boolean { try { const fd fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY) fs.writeSync(fd, content) fs.closeSync(fd) return true } catch (err) { promptAction.showToast({ message: 写入文件失败 }) return false } } // 文本追加写入日志专用 appendText(path: string, content: string): boolean { try { const fd fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.APPEND | fs.OpenMode.READ_WRITE) fs.writeSync(fd, content \n) fs.closeSync(fd) return true } catch (err) { return false } } // 读取完整文本 readText(path: string): string { if (!this.exists(path)) return try { const fd fs.openSync(path, fs.OpenMode.READ_ONLY) const stat fs.statSync(path) const buf new ArrayBuffer(stat.size) fs.readSync(fd, buf) fs.closeSync(fd) return String.fromCharCode(...new Uint8Array(buf)) } catch (err) { return } } // 删除单个文件 deleteFile(path: string): boolean { if (!this.exists(path)) return true try { fs.unlinkSync(path) return true } catch (err) { promptAction.showToast({ message: 文件删除失败 }) return false } } // 遍历目录返回所有文件名数组 listDirectory(dirPath: string): string[] { if (!this.exists(dirPath)) return [] try { return fs.readdirSync(dirPath) } catch (err) { return [] } } // 清空文件夹内所有文件保留文件夹 clearDir(dirPath: string) { const files this.listDirectory(dirPath) for (let name of files) { const fullPath dirPath / name this.deleteFile(fullPath) } } } export default FileUtil.getInstance()4.2 实战二本地日志记录页面文本读写追加etsimport FileUtil from ../utils/file_util Entry Component struct FileLogPage { State logContent: string logFilePath: string aboutToAppear() { FileUtil.setContext(getContext(this)) this.logFilePath FileUtil.getFilesRoot() /app_log.txt // 页面加载读取已有日志 this.logContent FileUtil.readText(this.logFilePath) } // 新增一条日志 addLog() { const time new Date().toLocaleString() const text 【${time}】用户操作页面 FileUtil.appendText(this.logFilePath, text) // 刷新显示 this.logContent FileUtil.readText(this.logFilePath) } // 清空全部日志 clearLog() { FileUtil.writeText(this.logFilePath, ) this.logContent } build() { Column({ space: 16 }) { Text(本地日志文件读写演示) .fontSize(22) .fontWeight(FontWeight.Bold) TextArea({ text: this.logContent, placeholder: 暂无日志记录 }) .width(95%) .layoutWeight(1) .backgroundColor(#fff) .fontSize(14) Row({ space: 20 }) { Button(添加日志记录).width(160).height(44).onClick(() this.addLog()) Button(清空日志).width(160).height(44).backgroundColor(#f56c6c).onClick(() this.clearLog()) } .width(100%) .justifyContent(FlexAlign.Center) .margin({ bottom: 20 }) } .width(100%) .height(100%) .padding(12) .backgroundColor(#f5f5f5) } }4.3 实战三相机图片缓存目录遍历页面etsimport FileUtil from ../utils/file_util Entry Component struct FileImageListPage { State imgNameList: string[] [] imageDir: string aboutToAppear() { FileUtil.setContext(getContext(this)) // 创建图片缓存文件夹 this.imageDir FileUtil.getFilesRoot() /camera_img FileUtil.mkdir(this.imageDir) // 读取目录下所有图片文件名 this.imgNameList FileUtil.listDirectory(this.imageDir) } // 清空全部缓存图片 clearAllImage() { FileUtil.clearDir(this.imageDir) this.imgNameList FileUtil.listDirectory(this.imageDir) } build() { Column({ space: 15 }) { Row() { Text(相机缓存图片列表).fontSize(20).layoutWeight(1) Button(清空缓存).backgroundColor(#f56c6c).onClick(() this.clearAllImage()) } .width(95%) List({ space: 8 }) { ForEach(this.imgNameList, (name: string) { ListItem() { Text(name) .width(100%) .padding(12) .backgroundColor(Color.White) .borderRadius(6) } }) } .layoutWeight(1) .width(95%) } .width(100%) .height(100%) .padding(15) .backgroundColor(#f5f5f5) } }五、API23 文件系统适配与 IO 性能优化规范5.1 文件句柄强制释放规范openSync 打开文件获取 fd 后无论读写成功失败必须执行 closeSync使用 try-catch 包裹文件操作catch 分支内也要关闭句柄防止泄漏批量循环读写文件不要重复打开关闭单次 fd 完成全部操作再释放。5.2 沙盒路径使用规范所有文件根目录统一通过页面上下文获取 filesDir/cacheDir禁止手写绝对路径图片、长期保存数据存 filesDir临时缓存、可清理文件存 cacheDir多层目录拼接统一使用/分隔工具内部统一路径拼接逻辑。5.3 IO 性能优化规范小于 10KB 小文本可使用同步读写日志、配置文件适用超过 100KB 图片、大文档禁止主线程同步一次性读取改用异步分片频繁追加日志不要每次打开关闭 fd可短时缓存 fd 统一批量写入再释放遍历文件夹删除文件先收集文件名循环逐个 unlinkSync不可直接 rmdirSync 非空目录。5.4 业务分层存储规范持久配置文本Preferences优先简单日志小文件fs 文本结构化多条数据RDB 数据库图片、离线本地资源fs filesDir 目录存储临时缓存、可自动清理文件cacheDir 目录。六、API23 升级高频兼容问题与解决方案问题 1文件写入后无法删除提示文件被占用 解决每次 openSync 操作结束一定调用 closeSync 释放 fd 句柄。问题 2路径不存在创建文件直接报错崩溃 解决操作前先用 existsSync 判断不存在先创建文件夹。问题 3读取文件中文乱码 解决读取 ArrayBuffer 后通过 Uint8Array 转字符串不直接截取二进制。问题 4rmdirSync 删除文件夹报错 解决该接口仅支持空目录先遍历删除内部所有文件再执行删除。问题 5升级 API23 后硬编码路径找不到文件 解决全部替换为 context.filesDir 动态获取沙盒根路径。问题 6多次写入同一文件内容错乱、覆盖丢失 解决追加模式使用 OpenMode.APPEND覆盖写入用 WRITE_ONLY区分两种写入模式。七、总结fs 文件系统是 OpenHarmony 沙盒本地文件唯一标准操作模块API Version23 强化文件句柄生命周期管理强制读写后关闭 fd统一沙盒路径获取方式解决旧版文件占用、内存泄漏、路径失效、大文件读取卡顿等核心兼容问题。项目封装通用文件工具类覆盖文件夹创建、文本读写追加、文件删除、目录遍历、缓存清空全套能力适配本地日志、相机图片缓存两大高频业务场景。