在这里插入图片描述
在这里插入图片描述

一、引言

文件管理是应用开发的基础能力。无论是缓存数据、导出文件、读取配置文件,还是处理用户上传的文档,都离不开文件系统操作。HarmonyOS 提供了强大的 fileIo 模块(@kit.CoreFileKit),支持文件的创建、读写、删除、复制、移动,以及目录的遍历、创建等操作。

本文将以一个文件管理器风格的演示页面为主线,深入讲解 fileIo 文件操作的核心 API,通过大量代码示例帮助读者掌握文件管理的开发技能。

二、文件系统基础

2.1 应用沙箱目录

HarmonyOS 应用运行在沙箱环境中,每个应用有自己的私有目录。常用目录如下:

目录 获取方式 用途
应用文件目录 context.filesDir 应用私有文件
应用缓存目录 context.cacheDir 缓存文件
应用数据库目录 context.databaseDir 数据库文件
应用偏好目录 context.preferencesDir Preferences 文件
应用临时目录 context.tempDir 临时文件
const ctx = getContext(this) as common.UIAbilityContext;
const filesDir = ctx.filesDir;      // 应用文件目录
const cacheDir = ctx.cacheDir;      // 缓存目录
const databaseDir = ctx.databaseDir; // 数据库目录

2.2 文件路径

HarmonyOS 文件路径使用 file:// 协议或直接使用绝对路径:

// 绝对路径
const filePath = `${ctx.filesDir}/demo.txt`;

// file:// 协议
const fileUrl = `file://${ctx.filesDir}/demo.txt`;

三、fileIo 核心 API

3.1 文件打开与创建

import { fileIo as fs } from '@kit.CoreFileKit';

// 打开文件(不存在则创建)
const file = fs.openSync('/path/to/file.txt', fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
// 使用文件描述符
const fd = file.fd;
// 关闭文件
fs.closeSync(file);

代码说明:

  • fs.openSync(path, mode) 同步打开文件,返回 File 对象。
  • OpenMode 枚举控制打开方式:
    • READ_ONLY:只读。
    • WRITE_ONLY:只写。
    • READ_WRITE:读写。
    • CREATE:不存在则创建。
    • TRUNC:清空内容。
    • APPEND:追加写入。
  • file.fd 获取文件描述符。
  • fs.closeSync(file) 关闭文件,释放资源。

3.2 文件写入

// 写入字符串
const file = fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
fs.writeSync(file.fd, 'Hello HarmonyOS!');
fs.closeSync(file);

代码说明:

  • fs.writeSync(fd, data) 同步写入数据,data 可以是字符串或 ArrayBuffer。
  • 写入后必须关闭文件,确保数据落盘。

3.3 文件读取

// 读取文本文件
const file = fs.openSync(path, fs.OpenMode.READ_ONLY);
const buffer = new ArrayBuffer(1024);
const readLen = fs.readSync(file.fd, buffer);
const text = String.fromCharCode(...new Uint8Array(buffer).slice(0, readLen));
fs.closeSync(file);

代码说明:

  • fs.readSync(fd, buffer) 同步读取数据到 buffer。
  • 返回实际读取的字节数。
  • 通过 Uint8ArrayString.fromCharCode 将二进制转换为文本。

3.4 文件信息

// 获取文件状态
const stat = fs.statSync(path);
console.info(`大小: ${stat.size} 字节`);
console.info(`是否目录: ${stat.isDirectory()}`);
console.info(`是否文件: ${stat.isFile()}`);
console.info(`修改时间: ${stat.mtime}`);

四、目录操作

4.1 创建目录

// 创建单级目录
fs.mkdirSync('/path/to/newDir');

// 递归创建多级目录
fs.mkdirSync('/path/to/a/b/c', true);

4.2 遍历目录

// 列出目录下所有文件名
const names = fs.listFileSync('/path/to/dir');
names.forEach((name) => {
  console.info(`文件名: ${name}`);
});

4.3 删除目录

// 删除空目录
fs.rmdirSync('/path/to/emptyDir');

// 递归删除目录
fs.rmdirSync('/path/to/dir', true);

五、实战代码:文件管理器页面

下面我们实现一个文件管理器风格的演示页面,包含工具栏、文件列表和文件操作。

5.1 定义数据结构

interface FileRow {
  name: string;
  size: string;
  type: string;
  icon: string;
}

代码说明:

FileRow 接口描述文件列表中的一行数据:

  • name:文件名。
  • size:文件大小(格式化后的字符串)。
  • type:文件类型(目录/文件)。
  • icon:图标(📁 或 📄)。

5.2 组件状态定义

@Entry
@Component
struct FilePage {
  @State files: FileRow[] = [];
  @State currentPath: string = '';
  private baseDir: string = '';

  aboutToAppear(): void {
    const ctx = getContext(this) as common.UIAbilityContext;
    this.baseDir = ctx.filesDir;
    this.currentPath = this.baseDir;
    this.refreshFiles();
  }

代码说明:

  • aboutToAppear 中获取应用文件目录作为初始路径。
  • 调用 refreshFiles 加载文件列表。

5.3 刷新文件列表

refreshFiles(): void {
  try {
    const list = fs.listFileSync(this.currentPath);
    this.files = list.map((name: string) => {
      const stat = fs.statSync(`${this.currentPath}/${name}`);
      return {
        name: name,
        size: stat.size > 1024 ? `${(stat.size / 1024).toFixed(1)} KB` : `${stat.size} B`,
        type: stat.isDirectory() ? '目录' : '文件',
        icon: stat.isDirectory() ? '📁' : '📄'
      };
    });
  } catch (e) {
    promptAction.showToast({ message: `读取失败: ${JSON.stringify(e)}` });
  }
}

代码说明:

refreshFiles 是文件列表加载的核心:

  1. 列出文件fs.listFileSync(this.currentPath) 列出当前目录下所有文件名。

  2. 映射为数据map 遍历文件名,对每个文件:

    • fs.statSync(path) 获取文件状态。
    • 格式化大小:大于 1KB 显示为 KB,否则显示为 B。
    • 判断类型:isDirectory() 区分目录和文件。
    • 设置图标:目录用 📁,文件用 📄。
  3. 异常处理:目录不存在或权限不足时捕获异常并提示。

5.4 创建文件

createFile(): void {
  try {
    const f = fs.openSync(`${this.currentPath}/demo_${Date.now()}.txt`, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
    fs.writeSync(f.fd, 'Hello HarmonyOS FileIO!\n这是一段写入文件的内容。');
    fs.closeSync(f);
    this.refreshFiles();
    promptAction.showToast({ message: '文件创建成功' });
  } catch (e) {
    promptAction.showToast({ message: `创建失败: ${JSON.stringify(e)}` });
  }
}

代码说明:

createFile 演示了文件创建和写入的完整流程:

  1. 打开文件fs.openSync 使用 CREATE | READ_WRITE 模式,文件不存在则创建。文件名使用 Date.now() 保证唯一。

  2. 写入内容fs.writeSync(f.fd, content) 写入文本内容。

  3. 关闭文件fs.closeSync(f) 关闭文件,确保数据落盘。

  4. 刷新列表:调用 refreshFiles 更新 UI。

  5. 异常处理:捕获并提示创建失败。

5.5 创建目录

createDir(): void {
  try {
    fs.mkdirSync(`${this.currentPath}/newDir_${Date.now()}`);
    this.refreshFiles();
    promptAction.showToast({ message: '目录创建成功' });
  } catch (e) {
    promptAction.showToast({ message: `创建失败: ${JSON.stringify(e)}` });
  }
}

代码说明:

createDir 创建新目录,使用 Date.now() 保证目录名唯一,创建后刷新列表。

5.6 删除文件

deleteItem(name: string): void {
  try {
    const stat = fs.statSync(`${this.currentPath}/${name}`);
    if (stat.isDirectory()) {
      fs.rmdirSync(`${this.currentPath}/${name}`);
    } else {
      fs.unlinkSync(`${this.currentPath}/${name}`);
    }
    this.refreshFiles();
    promptAction.showToast({ message: `已删除 ${name}` });
  } catch (e) {
    promptAction.showToast({ message: `删除失败: ${JSON.stringify(e)}` });
  }
}

代码说明:

deleteItem 演示了删除操作:

  1. 判断类型:通过 stat.isDirectory() 判断是目录还是文件。
  2. 删除目录fs.rmdirSync 删除目录。
  3. 删除文件fs.unlinkSync 删除文件。
  4. 刷新列表:更新 UI 并提示结果。

5.7 构建 UI

build() {
  Column() {
    // 顶部标题
    Column() {
      Text('FILE')
        .fontSize(12)
        .fontColor('#B8C0CC')
        .letterSpacing(6)
      Text('文件管理')
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .margin({ top: 6 })
      Text('fileIo · 文件读写与目录操作')
        .fontSize(12)
        .fontColor('#B8C0CC')
        .margin({ top: 6 })
    }
    .width('100%')
    .padding({ top: 44, bottom: 22 })
    .backgroundColor('#2F3542')

    // 工具栏
    Row({ space: 10 }) {
      Button('📄 新建文件')
        .height(38)
        .layoutWeight(1)
        .fontSize(12)
        .fontColor(Color.White)
        .backgroundColor('#57606F')
        .borderRadius(6)
        .onClick(() => { this.createFile(); })
      Button('📁 新建目录')
        .height(38)
        .layoutWeight(1)
        .fontSize(12)
        .fontColor(Color.White)
        .backgroundColor('#57606F')
        .borderRadius(6)
        .onClick(() => { this.createDir(); })
      Button('🔄 刷新')
        .height(38)
        .layoutWeight(1)
        .fontSize(12)
        .fontColor('#57606F')
        .backgroundColor(Color.White)
        .borderRadius(6)
        .border({ width: 1, color: '#57606F' })
        .onClick(() => { this.refreshFiles(); })
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#F1F2F6')

    // 当前路径
    Text(`📂 ${this.currentPath}`)
      .fontSize(11)
      .fontColor('#747D8C')
      .width('100%')
      .padding({ left: 16, right: 16, top: 8 })
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.MiddleTruncation })

    // 文件列表表格
    List({ space: 2 }) {
      ForEach(this.files, (file: FileRow) => {
        ListItem() {
          Row({ space: 12 }) {
            Text(file.icon)
              .fontSize(22)
            Column({ space: 3 }) {
              Text(file.name)
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
                .fontColor('#2F3542')
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
              Text(`${file.type} · ${file.size}`)
                .fontSize(11)
                .fontColor('#747D8C')
            }
            .alignItems(HorizontalAlign.Start)
            .layoutWeight(1)
            Button('删除')
              .fontSize(11)
              .fontColor('#FF4757')
              .backgroundColor('#FFF0F0')
              .borderRadius(6)
              .onClick(() => { this.deleteItem(file.name); })
          }
          .width('100%')
          .padding(12)
          .backgroundColor(Color.White)
          .border({ width: { bottom: 1 }, color: '#F1F2F6' })
        }
      })
    }
    .layoutWeight(1)
    .width('100%')

    // 底部返回
    Button('返回首页')
      .width('100%')
      .height(46)
      .fontSize(15)
      .fontColor(Color.White)
      .backgroundColor('#2F3542')
      .borderRadius(0)
      .onClick(() => { router.back(); })
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F1F2F6')
}

代码说明:

文件管理器页面结构:

  1. 顶部标题:深灰色背景的标题区。

  2. 工具栏:三个操作按钮(新建文件、新建目录、刷新),深灰色按钮配白色文字,刷新按钮为描边样式。

  3. 当前路径:显示当前所在目录路径,超出部分中间截断。

  4. 文件列表List 组件渲染文件列表,每行包含图标、文件名、类型和大小,右侧是删除按钮。

  5. 底部返回:全宽按钮返回首页。

六、文件读写高级用法

6.1 追加写入

const file = fs.openSync(path, fs.OpenMode.APPEND | fs.OpenMode.READ_WRITE);
fs.writeSync(file.fd, '追加的内容');
fs.closeSync(file);

6.2 文件复制

fs.copyFileSync(srcPath, destPath);

6.3 文件移动/重命名

fs.renameSync(oldPath, newPath);

6.4 文件截断

fs.truncateSync(path, 100);  // 截断到 100 字节

6.5 判断文件是否存在

import { common } from '@kit.AbilityKit';

function isExist(path: string): boolean {
  try {
    fs.statSync(path);
    return true;
  } catch (e) {
    return false;
  }
}

七、异步文件操作

对于大文件操作,推荐使用异步 API 避免阻塞主线程:

async function readLargeFile(path: string): Promise<string> {
  const file = fs.openSync(path, fs.OpenMode.READ_ONLY);
  try {
    const stat = fs.statSync(path);
    const buffer = new ArrayBuffer(stat.size);
    await fs.read(file.fd, buffer);
    return String.fromCharCode(...new Uint8Array(buffer));
  } finally {
    fs.closeSync(file);
  }
}

八、最佳实践

8.1 资源管理

文件操作完成后务必关闭文件(closeSync),避免文件描述符泄漏。

8.2 路径安全

拼接路径时注意分隔符,推荐使用模板字符串:

const filePath = `${this.currentPath}/${name}`;

8.3 大文件异步

大文件读写使用异步 API,避免阻塞 UI 线程。

8.4 异常处理

文件操作可能因权限、路径不存在等原因失败,务必使用 try...catch 处理。

九、常见问题

9.1 文件写入不生效

原因:写入后没有关闭文件,数据未落盘。

解决:写入后调用 closeSync

9.2 路径不存在报错

原因:目录或文件不存在。

解决:操作前检查路径,或使用 CREATE 模式自动创建。

9.3 权限不足

原因:访问了无权限的目录。

解决:只操作应用沙箱内的目录,或申请相应权限。

十、总结

本文深入讲解了 HarmonyOS 文件管理技术,通过一个文件管理器风格的演示页面实战演示了文件创建、写入、删除、目录操作等核心能力。

核心要点回顾:

  1. 应用沙箱目录:filesDir、cacheDir、databaseDir 等。
  2. openSync 打开/创建文件,OpenMode 控制模式。
  3. writeSync / readSync 读写文件。
  4. listFileSync 遍历目录,statSync 获取文件信息。
  5. mkdirSync / rmdirSync 管理目录。
  6. unlinkSync 删除文件,copyFileSync 复制文件。
  7. 文件操作后必须关闭,大文件使用异步 API。

文件管理是应用数据能力的基石,掌握它能构建文件浏览、缓存管理、数据导出等功能。下一篇我们将讲解 HarmonyOS 传感器开发。

Logo

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

更多推荐