应用沙箱与文件目录
鸿蒙每个应用都有独立的文件沙箱,只能读写自己沙箱内的文件,碰不到别的应用。这套隔离让恶意应用拿不到你的相册、文档,也让卸载应用时数据能干净地一起清掉。
沙箱是安全边界:应用内部随便放,跨边界必须走系统能力。
沙箱内按用途分成几个目录:应用数据目录存数据库和配置,缓存目录存临时文件,文件管理目录存用户可感知的文件。选对目录,卸载清理、备份恢复都有章可循。
刚接触鸿蒙文件开发的同学最容易犯的错,是拿路径当普通 Linux 路径直接用。沙箱路径每个应用都不一样,而且由系统分配,不能写死。正确的做法是运行时通过接口拿到自己的沙箱根,再在根上拼子路径。记住这一点,后面的代码就不会跑偏。
沙箱路径与 URI
获取应用沙箱根目录用 context 的 filesDir、cacheDir,或者通过文件管理接口拿沙箱路径。拿到的是 file:// 形式的 URI,转成普通路径就能配合文件系统接口使用。
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
const ctx = getContext(this) as common.UIAbilityContext;
const sandboxDir = ctx.filesDir; // /data/storage/el2/base/haps/entry/files
const cacheDir = ctx.cacheDir; // /data/storage/el2/base/haps/entry/cache
const fileUri = fileIo.openSync(sandboxDir + '/notes.txt', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
- filesDir正式数据,随备份走。
- cacheDir临时文件,系统可清。
- 沙箱根不同应用不同,别写死。
文件读写实战
读写文件用 fileIo 的 openSync、writeSync、readSync,或者更省事的文件流接口。写文本注意编码,读回来同样按 UTF-8 解码,避免中文乱码。
import { fileIo } from '@kit.CoreFileKit';
const file = fileIo.openSync(sandboxDir + '/diary.txt',
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
fileIo.writeSync(file.fd, '今天学了鸿蒙文件系统');
fileIo.fsyncSync(file.fd);
fileIo.closeSync(file);
const f2 = fileIo.openSync(sandboxDir + '/diary.txt', fileIo.OpenMode.READ_ONLY);
const buf = new ArrayBuffer(4096);
const n = fileIo.readSync(f2.fd, buf);
const bytes = new Uint8Array(buf.slice(0, n));
let text = '';
for (let i = 0; i < bytes.length; i++) text += String.fromCharCode(bytes[i]);
console.log('读到的内容', text);
fileIo.closeSync(f2);
写入后调用 fsync 把数据真正落到磁盘,防止系统掉电丢数据。小文件读写用同步接口简单直接;大文件用流式分段读写,避免一次性把整个文件装进内存。
图片与媒体文件存取
应用自己的图片可以放沙箱文件目录,用户想保存到相册,就要走 PhotoAccessHelper 或 FilePicker 交给系统,别自己往系统目录硬写。
FilePicker 的 DocumentViewPicker 可以把文件保存到用户选定的位置,用户全程可见、可撤销,符合系统对用户文件的保护要求。选完返回的文件 URI 保存在沙箱里,下次用再通过 URI 打开。
文件管理与备份
文件系统接口支持复制、移动、重命名、删除和列出目录。定期把正式数据复制到备份位置,或者把关键文件导到用户可见目录,用户体验会好很多。
fileIo.copyFileSync(sandboxDir + '/diary.txt', cacheDir + '/diary.bak');
fileIo.renameSync(cacheDir + '/diary.bak', cacheDir + '/diary-2026.bak');
const names = fileIo.listFileSync(sandboxDir);
console.log('目录内容', names);
删除文件用 unlinkSync,删完记得同步目录句柄。备份策略别太激进,一天一次全量加增量就够了,频繁写盘既耗电又伤闪存。
实战:缓存大文件
下载大文件先写缓存目录,分块写入并校验大小,完成后再决定要不要挪进正式目录。这样临时失败不会污染正式数据。
async function downloadToCache(url: string, target: string) {
const file = fileIo.openSync(cacheDir + '/' + target,
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC);
// 分块拉取写入,每块 writeSync 后记进度
// 全部完成后 closeSync,再 copyFileSync 到正式目录
fileIo.closeSync(file);
}
同时给文件加个过期时间,启动时清掉超过一周的缓存,控制磁盘占用。图片、视频这类可重建的缓存,清理优先级放最高。
小结
鸿蒙文件开发记住三条:沙箱是边界,目录要选对,用户文件走系统。正式数据放 filesDir、临时数据放 cacheDir、跨出沙箱用 FilePicker。读写记得 fsync,缓存记得定期清,文件这一关就稳了。