鸿蒙7新特性实战⑦:本地文件 CoreFileKit 全套操作指南(API26)
·

一、CoreFileKit 核心概述
HarmonyOS NEXT API26(鸿蒙7)将原分散的 @ohos.file.fs / @ohos.file.fileuri / @ohos.file.picker 全部收敛至 @kit.CoreFileKit,统一文件操作、URI转换、文件选择器、流式读写、跨应用文件分享、大文件拷贝进度监听全套能力。
1.1 核心优势
- 统一套件收口:仅需一次导入,覆盖文件全场景,告别多模块分散导入;
- 沙箱强隔离:应用私有目录天然隔离,无需额外权限;
- 原生流式大文件:
createStream分片读写,避免一次性加载大文件OOM; - 拷贝进度+取消能力:
TaskSignal支持监听进度、中途终止拷贝; - URI双向转换:沙箱路径 ↔ 文件URI,适配分享、相册、文件选择器;
- 同步/异步双接口:简单业务用同步,IO密集型推荐异步不阻塞主线程。
1.2 标准导入规范(@kit 统一解构导入,无默认导出)
// 全套常用成员一次性导入
import {
fileIo as fs,
fileUri,
picker,
TaskSignal,
FileOpenMode,
FileType,
BusinessError
} from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
1.3 应用沙箱五大核心目录(禁止硬编码路径,统一从context获取)
const ctx = getContext(this) as common.UIAbilityContext;
ctx.filesDir // 持久化业务数据目录(推荐存配置、日志)
ctx.cacheDir // 缓存目录(系统可自动清理,临时文件)
ctx.previewDir // 预览临时文件
ctx.tempDir // 临时文件
ctx.resourceDir // 应用资源目录
二、前置权限说明
- 沙箱内部文件:无需任何权限,应用私有目录自由读写;
- 访问系统相册/文档/下载(公共文件):需声明媒体权限:
"requestPermissions": [
{
"name": "ohos.permission.READ_IMAGEVIDEO",
"reason": "$string:read_media",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.WRITE_IMAGEVIDEO",
"reason": "$string:write_media",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
]
- 文件选择器picker:无需提前申请权限,拉起后由用户授权临时访问URI。
三、基础全套同步操作(小文件、简单业务)
3.1 判断文件/目录是否存在
async function exists(path: string): Promise<boolean> {
try {
fs.accessSync(path);
return true;
} catch {
return false;
}
}
3.2 创建目录(递归创建多级文件夹)
async function makeDir(path: string) {
if (!(await exists(path))) {
// recursive:true 自动创建父级目录
fs.mkdirSync(path, true);
}
}
3.3 文本写入(覆盖/追加)
// 覆盖写入
async function writeText(path: string, text: string) {
const fd = fs.openSync(path, FileOpenMode.READ_WRITE | FileOpenMode.CREATE);
const encoder = new TextEncoder();
const buf = encoder.encode(text);
fs.writeSync(fd, buf);
fs.closeSync(fd);
}
// 追加写入
async function appendText(path: string, text: string) {
const fd = fs.openSync(path, FileOpenMode.READ_WRITE | FileOpenMode.APPEND);
const buf = new TextEncoder().encode(text + "\n");
fs.writeSync(fd, buf);
fs.closeSync(fd);
}
3.4 读取文本文件
async function readText(path: string): Promise<string> {
const stat = fs.statSync(path);
const fd = fs.openSync(path, FileOpenMode.READ_ONLY);
const buffer = new ArrayBuffer(stat.size);
fs.readSync(fd, buffer);
fs.closeSync(fd);
return new TextDecoder().decode(buffer);
}
3.5 文件/目录删除
// 删除单个文件
async function deleteFile(path: string) {
if (await exists(path)) {
const stat = fs.statSync(path);
if (stat.isFile()) fs.unlinkSync(path);
}
}
// 递归删除文件夹
async function deleteDir(path: string) {
if (!(await exists(path))) return;
const stat = fs.statSync(path);
if (stat.isDirectory()) {
// 遍历子文件
const files = fs.listFileSync(path);
for (const f of files) {
await deleteDir(`${path}/${f}`);
}
fs.rmdirSync(path);
} else {
fs.unlinkSync(path);
}
}
3.6 文件复制 + 进度监听 + 取消(鸿蒙7增强)
async function copyFileWithProgress(src: string, dst: string) {
const signal = new TaskSignal();
const opt: fs.CopyOptions = {
progressListener: (p: fs.Progress) => {
const percent = (p.processedSize / p.totalSize * 100).toFixed(1);
hilog.info(0x0001, "Copy", `拷贝进度:${percent}%`);
// 中途取消 signal.cancel();
},
copySignal: signal
};
await fs.copy(src, dst, opt);
}
3.7 移动/重命名文件
async function moveFile(src: string, dst: string) {
await fs.move(src, dst);
}
3.8 遍历目录(过滤后缀、递归扫描)
async function listDir(path: string, suffix: string[] = [".txt"]) {
const list = fs.listFileSync(path, {
recursion: false,
filter: { suffix }
});
return list;
}
3.9 获取文件元信息(大小、创建时间、是否文件)
function getFileStat(path: string): fs.Stat {
return fs.statSync(path);
}
四、高级能力1:大文件流式读写(防止OOM,视频/日志/超大文件必备)
一次性读取GB级文件会造成内存峰值过高,使用 createStream 分片流式处理,内存稳定可控。
// 流式写入大文件
async function streamWriteLargeFile(filePath: string, chunkList: Uint8Array[]) {
const stream = fs.createStream(filePath, FileOpenMode.READ_WRITE | FileOpenMode.CREATE);
for (const chunk of chunkList) {
await stream.write(chunk);
}
await stream.flush();
await stream.close();
}
// 流式读取,逐块回调处理
async function streamReadLargeFile(filePath: string, chunkSize: number = 4096) {
const stat = fs.statSync(filePath);
const stream = fs.createStream(filePath, FileOpenMode.READ_ONLY);
let offset = 0;
while (offset < stat.size) {
const buf = new ArrayBuffer(chunkSize);
const readLen = await stream.read(buf, { offset });
offset += readLen;
// 业务处理当前块 new Uint8Array(buf,0,readLen)
}
await stream.close();
}
五、高级能力2:FileUri 路径与URI双向转换(分享、picker、跨应用)
5.1 沙箱路径 → 应用本地URI(用于分享、传给其他组件)
function pathToUri(path: string): string {
return fileUri.getUriFromPath(path);
}
5.2 URI → 真实沙箱路径(picker、分享回调拿到uri后转路径操作)
function uriToPath(uri: string): string {
return fileUri.getPathFromUri(uri);
}
六、高级能力3:文件选择器Picker(选图片/文档/保存文件)
6.1 选择图片(相册读取)
async function pickImage() {
const result = await picker.select({
MIMEType: FileType.IMAGE,
maxSelectNumber: 3
});
// 返回图片uri数组,转path后读取
const paths = result.photoUris.map(u => fileUri.getPathFromUri(u));
return paths;
}
6.2 保存文件到系统公共目录(文档/下载)
async function saveFileToPublic(srcPath: string, saveName: string) {
const opt = new picker.DocumentSaveOptions();
opt.newFileNames = [saveName];
opt.fileSuffixChoices = ["文本|.txt"];
const uris = await picker.save(opt);
const dstUri = uris[0];
const dstPath = fileUri.getPathFromUri(dstUri);
await fs.copy(srcPath, dstPath);
}
七、完整可运行页面示例(沙箱文件工具箱)

import { ComponentV2, Local } from '@kit.ArkUI';
import { fileIo as fs, fileUri, picker, FileOpenMode } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = "CoreFileDemo";
let basePath: string = "";
@Entry
@ComponentV2
struct FileKitDemoPage {
@State logText: string = "";
@State inputText: string = "测试文本内容";
aboutToAppear() {
const ctx = getContext(this) as common.UIAbilityContext;
basePath = `${ctx.filesDir}/file_demo`;
this.printLog(`沙箱工作目录:${basePath}`);
}
printLog(msg: string) {
this.logText += `\n${msg}`;
hilog.info(0x0002, TAG, msg);
}
// 1. 创建文件夹
async onCreateDir() {
try {
if (!(await this.exists(basePath))) {
fs.mkdirSync(basePath, true);
this.printLog("文件夹创建成功");
} else {
this.printLog("文件夹已存在");
}
} catch (e) {
this.printLog("创建目录失败:" + JSON.stringify(e));
}
}
async exists(path: string): Promise<boolean> {
try {
fs.accessSync(path);
return true;
} catch {
return false;
}
}
// 2. 写入文件
async onWriteFile() {
const filePath = `${basePath}/test.txt`;
try {
const fd = fs.openSync(filePath, FileOpenMode.READ_WRITE | FileOpenMode.CREATE);
const buf = new TextEncoder().encode(this.inputText);
fs.writeSync(fd, buf);
fs.closeSync(fd);
this.printLog("写入成功,路径:" + filePath);
} catch (e) {
this.printLog("写入失败:" + JSON.stringify(e));
}
}
// 3. 读取文件
async onReadFile() {
const filePath = `${basePath}/test.txt`;
if (!(await this.exists(filePath))) {
this.printLog("文件不存在");
return;
}
const stat = fs.statSync(filePath);
const fd = fs.openSync(filePath, FileOpenMode.READ_ONLY);
const buffer = new ArrayBuffer(stat.size);
fs.readSync(fd, buffer);
fs.closeSync(fd);
const content = new TextDecoder().decode(buffer);
this.printLog("读取内容:" + content);
}
// 4. 删除文件
async onDeleteFile() {
const filePath = `${basePath}/test.txt`;
if (await this.exists(filePath)) {
fs.unlinkSync(filePath);
this.printLog("文件已删除");
}
}
// 5. 选择相册图片
async onPickImage() {
try {
const res = await picker.select({ MIMEType: FileType.IMAGE, maxSelectNumber: 1 });
const uri = res.photoUris[0];
const path = fileUri.getPathFromUri(uri);
this.printLog(`选中图片路径:${path}`);
} catch (e) {
this.printLog("选图失败:" + JSON.stringify(e));
}
}
build() {
Column({ space: 12 }) {
Text("CoreFileKit 全套文件操作实战")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.width("90%")
.margin({ top: 20 });
TextInput({ text: this.inputText })
.width("90%")
.height(44)
.onChange(v => this.inputText = v);
Flex({ wrap: FlexWrap.Wrap, gap: 8 }) {
Button("1.创建目录").onClick(() => this.onCreateDir());
Button("2.写入文件").onClick(() => this.onWriteFile());
Button("3.读取文件").onClick(() => this.onReadFile());
Button("4.删除文件").onClick(() => this.onDeleteFile());
Button("5.选择相册图片").onClick(() => this.onPickImage());
}
.width("90%");
Text(this.logText)
.width("90%")
.height(300)
.padding(12)
.backgroundColor("#F5F7FA")
.borderRadius(8)
.fontSize(12)
.whiteSpace(WhiteSpace.PreWrap);
Blank();
Text("💡 沙箱文件无需权限,公共媒体文件需媒体读写权限")
.fontSize(12)
.fontColor("#888");
}
.width("100%")
.height("100%")
.padding({ bottom: 30 });
}
}
八、旧版 @ohos.file 迁移对照表
| 旧模块 | 新版CoreFileKit导入 | 变更说明 |
|---|---|---|
@ohos.file.fs |
import {fileIo as fs} from '@kit.CoreFileKit' |
全部文件读写、目录操作迁移至fileIo |
@ohos.file.fileuri |
fileUri 同套件内导出 |
路径/URI转换统一收口 |
@ohos.file.picker |
picker 同套件内导出 |
文件选择、保存弹窗 |
默认导入 import fs from '@ohos.file.fs' |
统一解构导入 {fileIo as fs} |
@kit无默认导出,必须大括号解构 |
九、高频踩坑避坑指南
坑1:直接硬编码绝对路径,升级/多设备失效
- 错误:
/data/storage/el2/base/files/xxx - 正确:永远通过
context.filesDir/cacheDir获取根目录拼接子路径
坑2:大文件一次性readSync,内存溢出崩溃
- 解决方案:超过10MB文件强制使用
createStream流式分片读写
坑3:文件打开后忘记closeSync/stream.close,文件句柄泄漏
- 规范:同步open后必须finally关闭;异步stream使用完毕主动close
坑4:picker拿到URI直接当作路径传给fs操作报错
- 原因:URI ≠ 沙箱路径,必须调用
fileUri.getPathFromUri(uri)转换后再操作
坑5:跨应用分享文件直接传路径,对方无法访问
- 正确:路径转URI
fileUri.getUriFromPath(path),传递URI给其他应用,系统临时授权访问
坑6:递归删除目录忘记判断是否存在,抛出异常崩溃
- 规范:所有文件操作前先
fs.accessSync判断存在,try-catch捕获文件异常
坑7:中文/特殊符号写入乱码
- 统一使用
TextEncoder/TextDecoderUTF-8编解码,禁止直接字符串转字节
坑8:主线程执行超大文件同步读写,页面卡顿掉帧
- 优化:大文件、拷贝、遍历全部使用异步API,放到子线程执行
十、生产环境最佳实践
- 目录分层规范
- filesDir:持久配置、用户笔记、本地缓存数据;
- cacheDir:临时下载、缩略图、可自动清理缓存;
- 内存管控:10MB以上文件强制流式读写,禁止一次性加载全量Buffer;
- 异常兜底:所有fs操作包裹try-catch,捕获文件不存在、权限、磁盘满等错误;
- 资源释放:fd、文件流使用完毕必须关闭,长期运行应用防止句柄泄漏;
- 缓存清理策略:app启动时清理cacheDir过期临时文件,避免存储占满;
- 跨应用文件:统一使用URI传递,不直接暴露沙箱真实路径,遵循系统安全授权模型;
- 批量拷贝:使用
TaskSignal增加进度UI与取消按钮,提升用户体验。
总结
CoreFileKit是鸿蒙7文件能力的统一标准套件,一套导入覆盖沙箱读写、目录管理、大文件流式、URI转换、文件选择器、带进度拷贝全场景。相比旧版分散的@ohos.file体系,接口更统一、流式能力更强、安全模型更清晰。
新项目直接全套使用@kit.CoreFileKit;存量项目按工具类、页面逐步迁移,优先替换底层文件工具层,上层业务无感知平滑切换。
更多推荐




所有评论(0)