在这里插入图片描述

一、CoreFileKit 核心概述

HarmonyOS NEXT API26(鸿蒙7)将原分散的 @ohos.file.fs / @ohos.file.fileuri / @ohos.file.picker 全部收敛至 @kit.CoreFileKit,统一文件操作、URI转换、文件选择器、流式读写、跨应用文件分享、大文件拷贝进度监听全套能力。

1.1 核心优势

  1. 统一套件收口:仅需一次导入,覆盖文件全场景,告别多模块分散导入;
  2. 沙箱强隔离:应用私有目录天然隔离,无需额外权限;
  3. 原生流式大文件createStream 分片读写,避免一次性加载大文件OOM;
  4. 拷贝进度+取消能力TaskSignal 支持监听进度、中途终止拷贝;
  5. URI双向转换:沙箱路径 ↔ 文件URI,适配分享、相册、文件选择器;
  6. 同步/异步双接口:简单业务用同步,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     // 应用资源目录

二、前置权限说明

  1. 沙箱内部文件:无需任何权限,应用私有目录自由读写;
  2. 访问系统相册/文档/下载(公共文件):需声明媒体权限:
"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" }
  }
]
  1. 文件选择器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 / TextDecoder UTF-8编解码,禁止直接字符串转字节

坑8:主线程执行超大文件同步读写,页面卡顿掉帧

  • 优化:大文件、拷贝、遍历全部使用异步API,放到子线程执行

十、生产环境最佳实践

  1. 目录分层规范
    • filesDir:持久配置、用户笔记、本地缓存数据;
    • cacheDir:临时下载、缩略图、可自动清理缓存;
  2. 内存管控:10MB以上文件强制流式读写,禁止一次性加载全量Buffer;
  3. 异常兜底:所有fs操作包裹try-catch,捕获文件不存在、权限、磁盘满等错误;
  4. 资源释放:fd、文件流使用完毕必须关闭,长期运行应用防止句柄泄漏;
  5. 缓存清理策略:app启动时清理cacheDir过期临时文件,避免存储占满;
  6. 跨应用文件:统一使用URI传递,不直接暴露沙箱真实路径,遵循系统安全授权模型;
  7. 批量拷贝:使用TaskSignal增加进度UI与取消按钮,提升用户体验。

总结

CoreFileKit是鸿蒙7文件能力的统一标准套件,一套导入覆盖沙箱读写、目录管理、大文件流式、URI转换、文件选择器、带进度拷贝全场景。相比旧版分散的@ohos.file体系,接口更统一、流式能力更强、安全模型更清晰。
新项目直接全套使用@kit.CoreFileKit;存量项目按工具类、页面逐步迁移,优先替换底层文件工具层,上层业务无感知平滑切换。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐