欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里插入图片描述

前言

在 Dart 中,dart:io 提供了基础的 FileDirectory 类。但在跨平台开发(特别是测试和 Web 支持)中,直接使用 dart:io 往往不够灵活。
例如,你想在单元测试中模拟文件系统,或者在不支持 dart:io 的环境(如 Web)中复用代码。

file 包是由 Dart 官方(Google)维护的一个文件系统抽象库。它定义了一套与 dart:io 完全一致的接口(FileSystemFileDirectory),但允许你切换底层实现(本地磁盘、内存、chroot 等)。

对于 OpenHarmony 开发者,这意味着你可以:

  1. 轻松编写单元测试:使用 MemoryFileSystem 模拟鸿蒙应用沙箱,无需真机读写。
  2. 统一路径处理:利用 FileSystem 抽象屏蔽鸿蒙与 Android/iOS 的路径差异。

一、核心组件

file 库的核心是 FileSystem 抽象类,它有几个主要实现:

类名 描述 鸿蒙应用场景
LocalFileSystem 封装了 dart:io,直接操作宿主系统文件。 生产环境,真实读写文件。
MemoryFileSystem 在内存中模拟完整的文件系统。 单元测试,临时数据缓存。
ChrootFileSystem 将文件操作限制在某个根目录下。 安全沙箱,防止越权访问系统文件。

FileSystem

+File file(path)

+Directory directory(path)

+Future isFile(path)

LocalFileSystem

+dart:io 实现

MemoryFileSystem

+Map 内存实现

ChrootFileSystem

+FileSystem delegate

+String root

二、OpenHarmony 适配与权限说明

在 OpenHarmony 上使用 LocalFileSystem 时,必须注意应用沙箱机制

  1. 权限声明:在 module.json5 中需按需声明权限(如 ohos.permission.READ_USER_STORAGE),但通常应用只能访问自己的私有目录。
  2. 路径限制:应用只能读写 Context 提供的路径(如 /data/storage/el2/base/haps/entry/files/)。
  3. Chroot 的妙用:建议使用 ChrootFileSystem 将根目录锁定在应用私有路径下,防止代码意外访问系统敏感目录导致 Crash。
// 鸿蒙推荐做法:锁定沙箱
import 'package:file/chroot.dart';
import 'package:file/local.dart';
import 'package:path_provider/path_provider.dart';

Future<FileSystem> getOhosFileSystem() async {
  final appDocDir = await getApplicationDocumentsDirectory();
  // 所有文件操作都被限制在 appDocDir 下
  return ChrootFileSystem(const LocalFileSystem(), appDocDir.path);
}

三、基础用例

3.1 切换本地与内存(测试神器)

import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:file/memory.dart';

void runOperation(FileSystem fs) {
  final file = fs.file('config.json');
  file.writeAsStringSync('{"theme": "dark"}');
  print('写入成功: ${file.path}');
}

void main() {
  // 生产环境:写真实磁盘
  runOperation(const LocalFileSystem());

  // 测试环境:只写内存,不产生垃圾文件,且速度极快
  runOperation(MemoryFileSystem());
}

在这里插入图片描述

3.2 目录监听(Watcher 替代方案)

虽然 file 库本身主要做 IO 抽象,但它遵循 dart:io 标准,也支持 watch()

void watchDir(FileSystem fs) {
  final dir = fs.directory('/logs');
  if (!dir.existsSync()) dir.createSync();

  dir.watch().listen((event) {
    print('文件变更: ${event.path} [${event.type}]');
  });
}

3.3 递归文件查找

Future<void> listFiles(FileSystem fs) async {
  final dir = fs.directory('.');
  
  await for (var entity in dir.list(recursive: true, followLinks: false)) {
    if (entity is File) {
      print('Found file: ${entity.basename}');
    }
  }
}

四、完整实战示例:鸿蒙日志管理系统

这个示例展示了如何设计一个日志管理器。在开发阶段使用 MemoryFileSystem 以便快速验证逻辑,在真机运行时切换到 LocalFileSystem 并结合 Chroot 保证安全。

import 'dart:convert';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:file/memory.dart';
import 'package:intl/intl.dart';

/// 日志管理器
class LogManager {
  final FileSystem fs;
  late final Directory _logDir;

  LogManager(this.fs, {String root = '/logs'}) {
    _logDir = fs.directory(root);
    if (!_logDir.existsSync()) {
      _logDir.createSync(recursive: true);
    }
  }

  /// 写入日志
  Future<void> log(String message, {String level = 'INFO'}) async {
    final now = DateTime.now();
    final fileName = '${DateFormat('yyyy-MM-dd').format(now)}.log';
    final file = _logDir.childFile(fileName);
    
    final timestamp = DateFormat('HH:mm:ss').format(now);
    final logLine = '[$timestamp] [$level] $message\n';

    // Append 模式写入
    await file.writeAsString(logLine, mode: FileMode.append);
  }

  /// 获取最近 N 天的日志内容
  Future<List<String>> getRecentLogs(int days) async {
    final logs = <String>[];
    final now = DateTime.now();

    for (var i = 0; i < days; i++) {
      final date = now.subtract(Duration(days: i));
      final fileName = '${DateFormat('yyyy-MM-dd').format(date)}.log';
      final file = _logDir.childFile(fileName);

      if (await file.exists()) {
        final content = await file.readAsString();
        logs.add('--- $fileName ---\n$content');
      }
    }
    return logs;
  }
  
  /// 清理旧日志
  Future<void> cleanOldLogs(int keepDays) async {
    // 简单实现:遍历所有文件,解析日期并删除(此处略去复杂解析)
    print('Cleaning logs older than $keepDays days...');
  }
}

// 模拟鸿蒙应用入口
void main() async {
  print('=== 启动模拟鸿蒙环境 (Memory Mode) ===');
  
  // 1. 使用内存文件系统模拟,无需真机权限
  final mockFs = MemoryFileSystem();
  final logger = LogManager(mockFs);

  // 2. 模拟写入操作
  await logger.log('应用启动', level: 'INFO');
  await logger.log('网络连接失败', level: 'ERROR');
  // 模拟等待
  await Future.delayed(Duration(milliseconds: 100));
  await logger.log('重试连接成功', level: 'INFO');

  // 3. 读取验证
  print('\n=== 读取最近日志 ===');
  final logs = await logger.getRecentLogs(1);
  logs.forEach(print);

  // 4. 验证文件结构
  print('\n=== 文件系统快照 ===');
  mockFs.directory('/logs').listSync().forEach((e) {
    print('File: ${e.path} (Size: ${(e as File).lengthSync()} bytes)');
  });
}

在这里插入图片描述

五、总结

file 包是 Dart 生态中被低估的基础设施。
对于 OpenHarmony 开发者来说,它不仅仅是一个文件操作库,更是解耦代码与底层 OS 的关键工具。
通过使用 FileSystem 接口注入,你的业务逻辑(日志、缓存、配置管理)将变得极易测试且安全,完全不必担心鸿蒙系统的路径变化或权限差异。

Logo

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

更多推荐