【鸿蒙心迹】HarmonyOS笔记26:本地文件与应用沙箱
🔥 个人主页: 杨利杰YJlio
❄️ 个人专栏: 《Windows 疑难杂症与工单复盘案例库》 《Sysinternals实战教程》
《WINDOWS教程》 《Windows PowerShell 实战》 《IOS插件分析测试》
🌟 让复杂的事情更简单,让重复的工作自动化


文章目录
大家好,我是杨利杰 YJlio。
上一节使用Preferences保存简单设置。这一节我把关注点转到文件:目录从哪里取得,应用可以访问什么范围,打开的文件如何关闭。
本篇编号026,计划日期2026年10月16日。原稿已保留。原稿的describeResult只处理显示文字,不包含文件访问;下方补充文件操作学习示例,尚未在本机编译或设备运行。图片均为概念示意。
一、先读写应用自己的练习文件
本例只操作应用文件目录中的note26.txt,不访问相册、下载目录或其他应用数据。先把自己的文件路径、内容与失败反馈核对清楚,再学习用户文件选择和授权。
沙箱限制应用可见的目录范围。应用自己的文件访问与用户文件访问是不同路径,不能通过拼接一个看起来像系统目录的字符串绕过访问边界。

二、按文件用途选择目录
| Context属性 | 本篇关注的用途 |
|---|---|
| filesDir | 普通应用文件,本例保存练习文本 |
| cacheDir | 可重新生成的缓存,可能被清理 |
| tempDir | 临时处理文件,不承担长期保留任务 |
| preferencesDir | 首选项相关文件,按Preferences接口管理 |
本例选择filesDir。缓存不适合存放唯一的重要数据;普通文件目录也不是永久备份。生命周期和访问条件可参见官方应用沙箱目录说明。

三、用Context取得路径,不写死设备目录
import { fileIo } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
function demoPath(context: common.UIAbilityContext): string {
return `${context.filesDir}/note26.txt`;
}
context应来自当前可用的UIAbility或页面宿主。函数参数不是路径字符串,也不是自己构造的空对象。本例使用UIAbilityContext对应的目录,不把它与ApplicationContext目录混为一谈。
拼接的是自己约定的固定文件名note26.txt,不接受用户提供的任意路径。调试工具看到的物理路径与应用看到的沙箱路径可能不同,所以不要把调试输出的物理根路径硬编码到业务代码。
目录由Context取得,文件名由本例固定,设备上的物理路径不写死。

四、写入前看清打开方式
async function writeDemo(context: common.UIAbilityContext): Promise<void> {
const path = demoPath(context);
const file = await fileIo.open(path,
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC);
try {
const text = 'Hello HarmonyOS';
const written = await fileIo.write(file.fd, text);
if (written !== text.length) {
throw new Error('练习文本未完整写入');
}
} finally {
await fileIo.close(file.fd);
}
}
CREATE让不存在的文件被创建;TRUNC在以写权限打开已有文件时把长度裁剪为零。本例每次写入都会替换练习文件的旧内容,不是追加。
written的单位是字节。这里固定为ASCII文本Hello HarmonyOS,每个字符对应一个UTF-8字节,才可与text.length比较。改成中文等内容时,要按编码后的字节长度处理,不能沿用这条比较。
TRUNC会清空已有内容;这段示例只用于可覆盖的note26.txt练习文件。
这不是原子更新或备份方案。中途写入失败可能留下空文件或不完整内容,不能用于直接覆盖重要用户文档。

五、读取文件,再整理显示结果
function describeResult(input: string): string {
return input.trim().length === 0 ? '暂无内容' : `已读取:${input}`;
}
async function readDemo(context: common.UIAbilityContext): Promise<string> {
const input = await fileIo.readText(demoPath(context));
return describeResult(input);
}
readText负责读取UTF-8文本;describeResult保留原稿的逻辑,只负责显示。全空白文本显示暂无内容,非空内容显示已读取前缀。前缀不代表函数自身完成了任何IO,实际读取发生在readDemo中。
文件不存在、无法访问或读取失败时,Promise会失败,调用方需要处理。不能将读取失败直接改写成暂无内容,这会掩盖真实原因。

六、打开成功后,用finally关闭
writeDemo在open成功之后进入try,因此写入或完整性检查失败也会经过finally。readText自行处理其内部文件访问,本例没有为它额外打开文件句柄。
页面调用可以按以下顺序记录结果:
import { BusinessError } from '@kit.BasicServicesKit';
async function runDemo(context: common.UIAbilityContext): Promise<string> {
try {
await writeDemo(context);
return await readDemo(context);
} catch (error) {
const err = error as BusinessError;
console.error(`file demo failed: ${err.code}, ${err.message}`);
return '文件操作失败,请检查日志';
}
}
本例的普通Error不一定有平台错误码,日志中应同时查看message;不要凭空补写错误编号。close自身也可能失败,实际排查需保留写入与关闭阶段的错误,不能只看最后一行。
上述代码块属于一组函数,应放在同一练习文件中组织使用;导入语句不要重复添加。页面获得可用上下文后调用runDemo,并把返回文字赋给观察状态。本文没有提供完整页面、文件选择器或后台任务。

七、应用文件与用户文档分别处理
自己的filesDir路径,不等于用户从文件选择器选择的URI。用户文件应按相应选择、授权与文件访问接口处理,不能把URI未经处理当作普通路径拼接。
本例没有实现用户文件访问,也没有读取其他应用的文件。清除应用数据、卸载相关包、切换上下文或环境,都可能改变原来的文件条件,不应承诺“保存后永远存在”。
先确认路径属于哪个上下文、哪类文件,再决定用哪套访问方式。

八、按路径、内容和失败记录核验
| 操作 | 预期或需记录内容 | 当前状态 |
|---|---|---|
| 取得demoPath | 记录实际filesDir与note26.txt | 待验证 |
| 写入练习文本 | 固定内容Hello HarmonyOS | 待验证 |
| 读取文本 | 预期已读取:Hello HarmonyOS | 待验证 |
| 单独检查空白显示 | describeResult对空白返回暂无内容 | 待验证 |
| 文件操作失败 | 显示失败并保留日志 | 待验证 |
| 正常重启后只调用readDemo | 核对原文件仍在与内容,避免先写再读掩盖结果 | 待验证 |
记录实际DevEco Studio、SDK/API、设备和操作顺序。第一次先写后读;若核对重启后的保留情况,则只读取,不能再次调用会覆盖文件的runDemo。
代码使用CoreFileKit的Promise形式,接口已参考官方基础文件IO文档,仍需在当前SDK中编译与适配。readText整段读取适用于本例的小文本,不扩展为大文件处理方案。

写在最后
这一节我把“文件访问”与“结果显示”拆开了:Context确定目录,固定文件名确定目标,open和write负责写入,readText负责读取,describeResult只处理展示。
最需要记住的是覆盖方式与资源关闭。下一篇学习关系型数据库,再区分一段文本文件与多条业务记录的存储需求。
参考资料
- OpenHarmony官方文档:应用沙箱目录,用于核对目录用途与路径边界。
- OpenHarmony官方文档:基础文件IO接口,用于核对open、write、close、readText与打开模式。实际HarmonyOS工程以对应SDK文档为准。
- 本系列第026篇原稿:《本地文件与应用沙箱初识》,计划日期2026-10-16。
声明:本文为个人学习笔记。代码和配图用于说明应用文件操作,尚未执行SDK编译或设备实测;写入、读取与重启结果均为待验证内容。

更多推荐



所有评论(0)