鸿蒙 ASCF 文件 API 避坑:internal 路径与编
文件操作是鸿蒙元服务开发绕不开的一环:缓存数据、保存图片、读取配置、解压文件都会用到。ASCF 的文件能力通过 has.getFileSystemManager() 暴露为 FileSystemManager,且是全局唯一单例,设计上与 RecorderManager、BackgroundAudioManager 类似。使用前先记两条硬规则:所有文件 API 的路径只支持 internal:// 开头的沙箱路径,不支持外部存储路径;相册等外部路径不能直接操作,通常要先 copyFile 到沙箱。一、获取实例与路径规则
const fs = has.getFileSystemManager();
路径只支持 internal://,这是后续所有读写、目录、解压操作的前提。
二、基础文件读写
写文件最常用的是 writeFile,data 支持 string 和 ArrayBuffer。写字符串可直接指定 encoding:
const fs = has.getFileSystemManager();
fs.writeFile({
filePath: 'internal://cache/data.txt',
data: 'Hello World',
encoding: 'utf-8',
success: () => {
console.info('写入成功');
},
fail: (err) => {
console.error('写入失败:', err);
}
});
同步写法用 writeFileSync。读文件用 readFile:传 encoding: 'utf-8' 返回字符串;不传 encoding 返回 ArrayBuffer。删除文件用 unlink;检查存在用 access;拷贝用 copyFile;重命名用 rename。
fs.readFile({
filePath: 'internal://cache/data.txt',
encoding: 'utf-8',
position: 0,
success: (res) => {
console.info('文件内容:', res.data);
},
fail: (err) => {
console.error('读取失败:', err);
}
});
fs.unlink({
filePath: 'internal://cache/data.txt',
success: () => {
console.info('删除成功');
}
});
fs.copyFile({
srcPath: 'internal://cache/temp.txt',
destPath: 'internal://files/saved.txt',
success: () => {
console.info('拷贝成功');
}
});
追加写入用 appendFile,适合日志,不用先读再拼接:
fs.appendFile({
filePath: 'internal://cache/log.txt',
data: '新的一行\n',
encoding: 'utf-8',
success: () => {
console.info('追加成功');
}
});
截断文件用 truncate,length 指定保留的字节数。
三、目录与文件信息
创建目录用 mkdir,recursive: true 可递归创建中间目录,类似 mkdir -p;读取目录用 readdir,返回 res.files;删除目录用 rmdir,recursive: true 会删除目录下所有文件和子目录。
fs.mkdir({
dirPath: 'internal://cache/mydir',
recursive: true,
success: () => {
console.info('目录创建成功');
}
});
fs.readdir({
dirPath: 'internal://cache/',
success: (res) => {
console.info('目录内容:', res.files);
}
});
获取文件大小用 getFileInfo,获取文件状态用 stat,Stats 对象提供 isFile() 和 isDirectory()。getSavedFileList 只列出通过 saveFile 保存的文件,包含 filePath、size、createTime,不包括 writeFile 创建的文件;要列出所有文件应使用 readdir。
四、高级 fd 读写、解压与文档预览
如果要精确控制读写位置或处理大文件,可以用文件描述符方式:openSync 打开,readSync 读取,writeSync 写入,closeSync 关闭。示例中 flag 使用 'r+' 表示读写模式。
const fs = has.getFileSystemManager();
const fd = fs.openSync({
filePath: 'internal://cache/data.bin',
flag: 'r+'
});
const readResult = fs.readSync({
fd: fd,
arrayBuffer: new ArrayBuffer(1024),
length: 1024
});
console.info('读取了', readResult.bytesRead, '字节');
fs.writeSync({
fd: fd,
data: '写入的数据',
encoding: 'utf-8'
});
fs.closeSync({ fd: fd });
解压用 unzip,只支持 zip 格式,不支持 tar.gz、rar 等;has.openDocument 可调起系统预览器打开文档,支持 pdf、doc、docx、xls、xlsx、ppt、pptx。
五、典型场景
缓存 JSON:写入用 writeFile,读取用 readFileSync,并处理解析异常。
日志追加:用 appendFile 写入时间戳和消息。
保存 chooseFile 文件:chooseFile 返回的临时路径不能直接读写,需要先 copyFile 到 internal://files/ 下的沙箱持久化目录。
has.chooseFile({
type: 'file',
count: 1,
success: (res) => {
const file = res.tempFiles;
const fs = has.getFileSystemManager();
const destPath = `internal://files/${file.name}`;
fs.copyFile({
srcPath: file.path,
destPath: destPath,
success: () => {
console.info('文件已保存:', destPath);
}
});
}
});
六、踩坑清单
1. 路径只支持 internal://,外部路径如相册路径不能直接操作,需要先 copyFile 到沙箱。
2. writeFile 默认覆盖写入,不是追加;要追加用 appendFile。
3. readFile 不传 encoding 时 res.data 是 ArrayBuffer,不是字符串;需要字符串就传 encoding: 'utf-8'。
4. 写图片、音频等二进制数据时,data 要传 ArrayBuffer,且不要传 encoding。
5. getSavedFileList 只列出 saveFile 保存的文件,不包括 writeFile 创建的文件;列出所有文件用 readdir。
6. 删文件用 unlink,删目录用 rmdir,搞反会报错。
7. unzip 只支持 zip,不支持 tar.gz、rar 等格式。
从使用频率看,日常开发最常用的是 writeFile、readFile、appendFile、unlink、copyFile、mkdir、readdir。如果只是存简单 key-value 数据,用 has.setStorage / has.getStorage 就够了,不必折腾文件 API;文件 API 更适合存大数据、二进制文件、日志这类场景。把 internal:// 路径限制、readFile 的 encoding 行为、writeFile 与 appendFile 的区别记住,基本就能避开主要问题。
Re: 鸿蒙 ASCF 文件 API 避坑:internal 路径与编
整理得挺全面,尤其 internal:// 沙箱路径这条很关键,外部路径不能直接操作、相册文件要先 copyFile 到沙箱,确实容易踩坑。基础读写、追加日志、递归建目录、rmdir recursive 这些示例也很直观,fd 读写对处理大文件或精确位置挺有参考价值。最后 chooseFile 保存到 internal: 那段好像没写完,const file = res.tempFiles; 后面就断了,方便的话可以补一下后续 copyFile 和持久化处理的完整流程。另外 getSavedFileList 只列 saveFile 保存的文件、不包括 writeFile 创建的,这个提醒很实用。感谢分享,收藏了。Re: 鸿蒙 ASCF 文件 API 避坑:internal 路径与编
帖子写得很实在,路径规则和 API 清单基本覆盖了日常用得到的部分,尤其是 getSavedFileList 那条,确实容易踩——很多人以为它能列出沙箱里所有文件,结果 writeFile 写进去的怎么都查不到,排查半天。 补充几个我实际踩过的坑: 一是 cache 和 files 的区分。internal: 属于缓存目录,系统在空间紧张时会主动清理,只适合放临时数据;要持久保存的图片、配置、下载文件建议放 internal: 被清之后再用 readFile 读会直接 fail,所以读的地方最好都兜一下 fail 分支。 二是 readFile 忘了传 encoding。不传返回的是 ArrayBuffer,读文本会拿到一串字节,如果代码里还顺手做了字符串拼接或者 JSON.parse,报错信息看着毫不相关。同理 readFileSync 解 JSON 一定要包 try/catch,元服务里异常抛出来体验很差。 三是大文件不要用同步接口。readFileSync、writeFileSync 在主线程上跑,文件稍大就卡界面,几百 KB 以上建议走 fd 分片读,配合 position 控制偏移,或者干脆用异步版本。示例里 openSync 的 flag 用 r+ 有个前提,文件必须已存在,想在打开时自动创建得用 a+ 或先 access 判断一下。 四是 unzip 只吃 zip,后Re: 鸿蒙 ASCF 文件 API 避坑:internal 路径与编
感谢楼主整理,这份避坑很实用。internal:// 沙箱路径这条确实容易踩,外部路径不能直接操作、要先 copyFile 到沙箱,这个点很关键。读写、追加、截断、目录、fd、解压和文档预览都覆盖到了,尤其是 getSavedFileList 只列 saveFile 保存的文件,不包括 writeFile 创建的,还有 unzip 只支持 zip,这两个细节很容易搞混。chooseFile 返回的临时路径不能直接读写,要先 copyFile 到 internal: 持久化,也很提醒人。想问下 appendFile 写日志时,如果文件不存在会自动创建吗,还是需要先 writeFile?整体写得很清楚,收藏了。
页:
[1]