第7.16篇:Core File Kit——沙箱目录全局共享

难度:⭐⭐ 进阶
前置知识:第 2.9 篇 视频导出与本地保存
涉及源文件products/default/src/main/ets/services/VideoExportService.ets


概述

在传统操作系统中,每个应用都运行在独立的沙箱环境中——应用 A 无法直接访问应用 B 的文件,反之亦然。这种设计保障了安全隔离,却也带来了一个长期困扰开发者和用户的痛点:跨应用文件共享难

想象在"画伴梦工厂"中的使用场景:孩子创作了一段精彩的动画视频,家长希望将这个视频插入到 WPS 文档中,或者发送到微信家庭群。在传统模式下,应用需要先通过 DocumentViewPicker 将视频"导出"到用户指定的目录(如 Documents),然后用户再手动从该目录中选取文件进行下一步操作。这个"导出再导入"的流程在用户体验上存在明显的断裂感。

HarmonyOS 7(API 26)在 HDC 2026 上推出的 Core File Kit 沙箱目录全局共享 能力,彻底改变了这一局面。它赋予了应用沙箱内特定目录以系统级可见性——这些目录中的文件可以被系统中的其他应用直接访问,无需"导出"和"导入"的中间步骤。再配合统一的 fileUri 路径管理,跨应用文件共享变得像访问本地文件一样自然。

本文将深入解析 Core File Kit 沙箱目录全局共享的技术原理、API 使用方式、权限模型,并探讨如何将项目现有的 VideoExportService(基于 DocumentViewPicker)升级为更简洁的 Core File Kit 方案。


一、传统沙箱的困局

1.1 应用沙箱隔离设计

在 HarmonyOS 的沙箱机制中,每个应用在安装时都会获得一个独立的沙箱目录,结构如下:

/data/app/el2/100/base/{bundleName}/
  ├── cache/        ← 缓存目录,应用私有
  ├── files/        ← 文件目录,应用私有
  ├── temp/         ← 临时目录,应用私有
  └── databases/    ← 数据库目录,应用私有

这些目录中的文件,其他应用完全不可见。这种隔离是系统安全的基石——恶意应用无法窥探其他应用的数据。但对于"共享"这一需求,隔离就变成了障碍。

1.2 传统共享路径:DocumentViewPicker 的问题

在第 2.9 篇中,我们实现了基于 DocumentViewPicker 的视频导出流程:

应用沙箱 → DocumentViewPicker.save() → 用户选择位置 → 拷贝到目标位置 → 其他应用访问

这个流程的痛点很明显:

问题 表现
步骤冗余 用户需要点击保存→选择位置→等待拷贝,至少 3 步操作
用户决策负担 必须自行选择保存路径,很多用户不知道文件应该存到哪里
文件副本膨胀 沙箱中一份副本,导出位置另一份副本,占用双倍存储空间
无法直接引用 其他应用无法直接引用沙箱中的文件,必须经过拷贝
流程断点 保存完成后,用户需要手动切换到目标应用并找到文件

对于"画伴梦工厂"的用户而言,这意味着:创作动画 → 导出保存 → 退出应用 → 打开微信 → 找到文件 → 发送 的六步流程。这种体验在 2026 年的 HarmonyOS 7 上,显然不够好。


二、Core File Kit 沙箱目录全局共享

2.1 核心思想:让沙箱的一部分"可见"

Core File Kit 的沙箱目录全局共享并非完全打破沙箱隔离——那将是一个安全灾难。它的设计思想更加精妙:在沙箱中划出一个"共享区域",该区域中的文件对系统和其他应用可见

应用沙箱
┌─────────────────────────────────────────────┐
│                                             │
│  cacheDir/    filesDir/    tempDir/          │
│  ┌───────┐   ┌───────┐   ┌───────┐          │
│  │ 私有  │   │ 私有  │   │ 私有  │  ← 其他应用不可见  │
│  └───────┘   └───────┘   └───────┘          │
│                                             │
│  sharedDir/ (新增)                            │
│  ┌─────────────────────────────────┐         │
│  │ 共享目录:系统级可见             │  ← 其他应用可直接访问  │
│  │ ├── kid_animation_1689.mp4      │         │
│  │ ├── my_drawing_1690.png         │         │
│  │ └── temp_share_1691.jpg         │         │
│  └─────────────────────────────────┘         │
│                                             │
└─────────────────────────────────────────────┘

2.2 共享目录的访问路径

对于其他应用而言,共享目录中的文件可以通过统一的 fileUri 格式访问:

file://docs/storage/Users/currentUser/Documents/{bundleName}/{fileName}

这个路径是系统级可解析的——任何应用只要获取到这个 URI,就可以通过 fileIo API 直接读写该文件,而无需经过任何"导出"流程。

2.3 与 cacheDirfilesDir 的核心区别

维度 cacheDir / filesDir sharedDir(全局共享目录)
可见性 仅本应用可见 全局系统可见
访问方式 仅本应用 fileIo 访问 任何应用 fileIo 访问
生命周期 应用卸载时删除 应用卸载时删除
是否需要导出 需要(DocumentViewPicker) 无需导出
典型用途 缓存、私有配置 跨应用共享文件
配额限制 与沙箱总配额共享 独立于应用沙箱配额之外

最关键的区别在于:sharedDir 中的文件不需要经过任何"导出"操作即可被其他应用访问。这意味着在"画伴梦工厂"中,用户创作的动画视频一旦保存到 sharedDir,就可以被微信、WPS、相册等任何应用直接打开,就像访问用户 Documents 目录中的文件一样自然。


三、统一 fileUri 路径管理

3.1 fileUri 协议升级

在 HarmonyOS 7 中,fileUri 从简单的路径转 URI 工具升级为统一文件资源标识符管理系统。沙箱共享目录的文件 URI 格式如下:

file://docs/storage/Users/{userId}/Documents/{bundleName}/{relativePath}

这个格式包含三层关键信息:

URI 片段 含义 示例
docs/storage/Users/{userId}/Documents/ 系统 Documents 根目录 指向用户的文档存储根
{bundleName} 来源应用的包名 com.dreamworks.drawing
{relativePath} 共享目录内的相对路径 videos/kid_animation.mp4

这种结构确保了 URI 的全局唯一性可溯源——任何应用看到这个 URI,都能知道文件来自哪个应用的沙箱共享目录。

3.2 兼容传统 URI

Core File Kit 保持了向后兼容。传统的 file:///data/... 路径和 file://docs/... 路径可以共存,系统会在底层自动处理路径映射:

import { fileUri } from '@kit.CoreFileKit';

// 传统路径(向后兼容)
const oldUri = fileUri.getUriFromPath('/data/storage/el2/base/cache/demo.mp4');
// 输出: file:///data/storage/el2/base/cache/demo.mp4

// 共享目录路径(HarmonyOS 7 新格式)
const sharedUri = fileUri.getUriFromPath('/data/storage/el2/base/shared/videos/demo.mp4');
// 输出: file://docs/storage/Users/100/Documents/com.dreamworks.drawing/videos/demo.mp4

两种格式的 URI 都可以通过 fileIo API 正常读写,但只有共享目录的 URI 才能被其他应用解析和访问

3.3 项目中的 fileUri 使用

VideoExportService.ets 中,我们已经在使用 fileUri.getUriFromPath

const sourcePath = VideoExportService.toLocalPath(videoUri);
return {
  path: sourcePath,
  uri: fileUri.getUriFromPath(sourcePath),  // → file:// 格式
  fileName: fileName
};

升级到 Core File Kit 后,这段代码几乎不需要改动——唯一的变化是 sourcePath 将从私有沙箱目录变为共享目录,fileUri.getUriFromPath 会自动生成系统可解析的共享 URI。


四、API 概览

Core File Kit 的沙箱目录全局共享提供了以下核心 API。

4.1 获取共享目录路径

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

const context = getContext() as common.UIAbilityContext;

// 获取共享目录路径(与应用同级目录结构)
const sharedPath = context.cacheDir.replace('cache', 'shared');
// 或使用系统推荐路径
const sharedDir = `${context.filesDir}/shared/`;

⚠️ 注意:不同 HarmonyOS 版本的共享目录路径可能有差异。建议使用 fileIo 推荐的路径获取方式。

4.2 创建共享文件

// 在共享目录中创建新文件
const sharedFileUri = await fileIo.createFile(sharedDir, 'kid_animation.mp4');
// 返回: file://docs/storage/Users/100/Documents/com.dreamworks.drawing/kid_animation.mp4

// 写入内容
const file = fileIo.openSync(sharedFileUri, fileIo.OpenMode.READ_WRITE);
try {
  fileIo.writeSync(file.fd, videoBuffer);
} finally {
  fileIo.closeSync(file);
}

4.3 打开共享文件

// 其他应用通过 URI 打开共享文件
const file = fileIo.openSync(
  'file://docs/storage/Users/100/Documents/com.dreamworks.drawing/kid_animation.mp4',
  fileIo.OpenMode.READ_ONLY
);
try {
  const stat = fileIo.statSync(file.fd);
  const buffer = new ArrayBuffer(stat.size);
  fileIo.readSync(file.fd, buffer);
} finally {
  fileIo.closeSync(file);
}

4.4 获取文件信息

const stat = fileIo.statSync(sharedFileUri);
console.info(`文件大小: ${stat.size}`);
console.info(`修改时间: ${stat.mtime}`);
console.info(`文件类型: ${stat.isFile() ? '文件' : '目录'}`);

4.5 删除共享文件

await fileIo.deleteFile(sharedFileUri);
// 或使用 sync 版本
fileIo.deleteFileSync(sharedFileUri);

4.6 共享目录列表

// 列出共享目录中的所有文件
const files = fileIo.listFileSync(sharedDir);
for (const file of files) {
  console.info(`共享文件: ${file.name}, 大小: ${file.size}`);
}

五、权限模型

5.1 基于 URI 的授权访问

Core File Kit 的沙箱目录全局共享采用 URI 授权(URI Permission) 机制。一个应用访问其他应用的共享文件时,不需要申请 READ_MEDIA 等存储权限,而是通过获取文件的 URI 来实现授权。

应用 A(创建者)                应用 B(访问者)
     │                              │
     │ 写入文件到 sharedDir          │
     │ 获得 fileUri                  │
     │                              │
     │ ─── 传递 fileUri ──────────→ │
     │   (通过分享、剪贴板、数据通道)│
     │                              │
     │                              │ 通过 fileUri 直接打开文件
     │                              │ fileIo.openSync(uri, READ_ONLY)
     │                              │
     │                              │ 读取成功,无需额外权限

5.2 权限授予方式

方式 说明 适用场景
通过 systemShare 分享 URI 共享 URI 本身,而非拷贝文件 用户点击分享时自动传递 URI
通过剪贴板传递 将 URI 复制到系统剪贴板 同设备内快速传递
通过分布式软总线传递 跨设备传递 URI 多设备协同场景
通过 Intent 附加 在 Want 参数中附带 URI 应用间通信

每种方式都经过了系统安全策略的校验——未经过用户确认的 URI 传递将被系统拦截,防止恶意应用窃取文件引用。

5.3 权限生命周期

维度 策略
有效期 与文件生命周期一致,文件删除后 URI 自动失效
作用域 单文件级别,不能通过一个 URI 访问整个共享目录
可撤销 文件创建者可以随时删除文件,使所有 URI 失效
继承性 子目录和文件的 URI 需独立获取,不继承父目录权限

5.4 与传统存储权限的对比

维度 传统存储权限 Core File Kit URI 授权
权限类型 全局权限(READ_MEDIA、WRITE_MEDIA) 细粒度 URI 授权
用户确认 安装时一次授权,或运行时弹窗 每次分享都需用户确认
访问范围 整个媒体库或存储空间 单文件精确范围
撤销方式 系统设置中手动撤销 删除文件即自动撤销
隐私保护 应用可扫描整个媒体库 应用只知道它被显式告知的 URI

从隐私保护的角度看,URI 授权模式远比传统存储权限更安全。在"画伴梦工厂"中,其他应用只能访问用户主动分享出去的视频文件,无法扫描到应用沙箱中的其他作品草稿。


六、在项目中的应用——升级 VideoExportService

6.1 当前架构回顾

在第 2.9 篇中,VideoExportService 的保存流程如下:

prepareVideo()
    ↓ 准备视频到 cacheDir
video 准备完成(cacheDir 中)
    ↓
DocumentViewPicker.save()
    ↓ 用户选择位置
copyFileToUri()
    ↓ 从 cacheDir 拷贝到用户选择的位置
保存完成

这个流程的核心问题是:文件需要在沙箱和用户选择的目标位置之间做一次完整的拷贝。对于 4K 动画视频(可能达到数百 MB),拷贝操作既耗时又占用存储空间。

6.2 Core File Kit 升级方案

升级后的流程将不再需要 DocumentViewPicker 和文件拷贝:

prepareVideo()
    ↓ 准备视频到 sharedDir(共享目录)
video 准备完成(sharedDir 中,系统级可见)
    ↓
获取共享 fileUri
    ↓(用户可直接通过其他应用打开该 URI)
保存完成(无需 DocumentViewPicker,无需拷贝)

6.3 代码实现对比

传统方案(当前 VideoExportService):

// 文件写入 cacheDir(私有)
const targetPath = context.cacheDir + '/' + fileName;
VideoExportService.writeUint8Array(targetPath, content);

// 需要 DocumentViewPicker 导出
const documentPicker = new picker.DocumentViewPicker(context);
const savedUris = await documentPicker.save(options);

// 需要文件拷贝
VideoExportService.copyFileToUri(prepared.path, savedUris[0]);

Core File Kit 升级方案:

import { fileIo, fileUri } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';

// 获取共享目录路径
const sharedDir = context.cacheDir.replace('cache', 'shared');
// 确保共享目录存在
fileIo.mkdirSync(sharedDir, true);

// 将文件写入共享目录
const sharedPath = sharedDir + '/' + fileName;
VideoExportService.writeUint8Array(sharedPath, content);

// 获取共享 URI(系统级可见)
const sharedUri = fileUri.getUriFromPath(sharedPath);

// 此时,其他应用已经可以通过 sharedUri 访问该文件
// 无需 DocumentViewPicker,无需 copyFileToUri
return {
  path: sharedPath,
  uri: sharedUri,
  fileName: fileName
};

核心变化总结:

方面 传统方案 Core File Kit 方案
写入位置 cacheDir(私有) sharedDir(全局可见)
导出步骤 需要 DocumentViewPicker 不需要
文件拷贝 需要 copyFileToUri 不需要
用户操作 选择保存位置 无操作
其他应用访问 需要先找到导出的文件 直接通过 URI 访问
磁盘空间 两份副本 一份副本

6.4 分享流程简化

在传统方案中,showSystemShare 方法需要先准备视频到 cacheDir,然后通过 systemShare.ShareController 分享:

static async showSystemShare(videoUri, title, story, rawFilePath) {
  // 准备视频(写入 cacheDir)
  const prepared = await prepareVideo(videoUri, title, rawFilePath);
  // 构建分享数据
  const data = await buildSharedData(videoUri, title, story, rawFilePath);
  const controller = new systemShare.ShareController(data);
  await controller.show(context, options);
}

升级后,分享流程可以直接传递共享 URI:

static async showSystemShareWithCoreFileKit(videoUri, title, story, rawFilePath) {
  // 准备视频(写入 sharedDir,直接获得共享 URI)
  const prepared = await prepareVideoToSharedDir(videoUri, title, rawFilePath);
  
  // 直接使用共享 URI 构建分享数据
  const data = new systemShare.SharedData({
    utd: uniformTypeDescriptor.UniformDataType.MPEG4,
    uri: prepared.uri,  // 共享 URI,系统可解析
    title: title,
    description: story
  });
  
  const controller = new systemShare.ShareController(data);
  await controller.show(context, options);
  // 分享时传递的是 URI 而非文件副本
  // 接收方应用直接通过 URI 读取文件
}

这种模式下,分享操作不再需要文件拷贝——系统分享面板的接收方应用直接通过 URI 读取共享目录中的文件,零拷贝、零延迟。


七、应用场景

7.1 跨应用文件直接共享

场景 传统流程 Core File Kit 流程
视频分享到微信 导出 → 切换到微信 → 选择文件 → 发送 直接点击分享 → 选择微信 → 发送
插入到 WPS 文档 导出 → 切换到 WPS → 插入文件 → 选择导出的文件 WPS 直接打开共享 URI → 插入
导入到相册 导出 → 打开相册 → 检查新文件 相册直接发现共享文件
发送到打印机 导出 → 切换到打印应用 → 选择文件 打印应用直接读取共享 URI

7.2 Content Provider 场景

Core File Kit 的沙箱共享目录天然支持 Content Provider 模式——应用可以将共享目录注册为内容提供者的数据源,其他应用通过 Content Provider API 查询和访问:

// 注册共享目录为 Content Provider
// module.json5 中配置
{
  "module": {
    "abilities": [{
      "name": "FileShareAbility",
      "type": "content",
      "uri": "file://docs/storage/Users/100/Documents/com.dreamworks.drawing/",
      "permissions": ["ohos.permission.READ_SHARED_FILE"]
    }]
  }
}
// 其他应用通过 Content Provider 访问
import { dataShare } from '@kit.DataShareKit';

const uri = 'file://docs/storage/Users/100/Documents/com.dreamworks.drawing/kid_animation.mp4';
const file = await dataShare.openFile(uri, 'r');
// 直接读取共享文件

7.3 多设备共享

配合第 7.15 篇的分布式软总线 2.0,共享 URI 可以跨设备传递:

手机(画伴梦工厂)                   平板(WPS 文档)
     │                                  │
     │ 保存视频到 sharedDir              │
     │ 获取共享 URI                      │
     │                                  │
     │ ── 碰一碰传递 URI(7.14) ──────→ │
     │                                  │
     │                                  │ 通过 URI 读取视频
     │                                  │ 直接插入到文档中

在这个场景中,跨设备传输的不是大文件(可能数百 MB),而是一个几十字节的 URI 字符串。真正的文件数据仍然在源设备的共享目录中,接收端通过分布式文件系统按需读取。这比传统"发送文件"模式节省了大量的传输时间和流量。


八、从传统 fileIo 到 Core File Kit 的迁移路径

8.1 渐进式迁移策略

对于现有项目,迁移到 Core File Kit 不必一步到位。推荐采用渐进式策略:

阶段一:新增共享路径(最低风险)
  ├── 保留现有 cacheDir 逻辑不动
  └── 新增 sharedDir 写入方法

阶段二:双路径写入(平稳过渡)
  ├── 写入 sharedDir(新流程)
  └── 保留 cacheDir 写入作为 fallback

阶段三:完全迁移(清理旧代码)
  ├── 移除 DocumentViewPicker 依赖
  ├── 移除 copyFileToUri 方法
  └── 所有导出走共享目录

8.2 具体迁移步骤

第一步:创建共享目录工具方法

export class CoreFileKitHelper {
  static getSharedDir(context: common.UIAbilityContext): string {
    // shared 目录与应用沙箱同级
    const sharedPath = context.cacheDir.replace('cache', 'shared');
    if (!fileIo.accessSync(sharedPath)) {
      fileIo.mkdirSync(sharedPath, true);
    }
    return sharedPath;
  }

  static getSharedUri(context: common.UIAbilityContext, fileName: string): string {
    const sharedDir = CoreFileKitHelper.getSharedDir(context);
    const filePath = sharedDir + '/' + fileName;
    return fileUri.getUriFromPath(filePath);
  }
}

第二步:在 VideoExportService 中新增共享方法

// 新增:保存到共享目录(不需要 DocumentViewPicker)
static async saveToSharedDir(
  videoUri: Resource | string,
  title: string,
  rawFilePath: string
): Promise<string> {
  const context = getContext() as common.UIAbilityContext;
  const prepared = await VideoExportService.prepareVideo(videoUri, title, rawFilePath);

  // 将文件从 cacheDir 复制到 sharedDir
  const sharedDir = CoreFileKitHelper.getSharedDir(context);
  const sharedPath = sharedDir + '/' + prepared.fileName;
  VideoExportService.copyFileSync(prepared.path, sharedPath);

  // 返回共享 URI
  return fileUri.getUriFromPath(sharedPath);
}

// 新增:文件拷贝辅助方法
private static copyFileSync(src: string, dest: string): void {
  const srcFile = fileIo.openSync(src, fileIo.OpenMode.READ_ONLY);
  try {
    const stat = fileIo.statSync(srcFile.fd);
    const buffer = new ArrayBuffer(stat.size);
    fileIo.readSync(srcFile.fd, buffer);

    const destFile = fileIo.openSync(
      dest,
      fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.TRUNC
    );
    try {
      fileIo.writeSync(destFile.fd, buffer);
    } finally {
      fileIo.closeSync(destFile);
    }
  } finally {
    fileIo.closeSync(srcFile);
  }
}

第三步:页面中切换使用

// 传统保存(保留兼容)
private async saveCurrentVideoLegacy(): Promise<void> {
  await VideoExportService.saveToLocal(...);
}

// 新式保存(共享目录,一步到位)
private async saveCurrentVideoShared(): Promise<void> {
  const uri = await VideoExportService.saveToSharedDir(
    this.getCurrentVideo(),
    this.getCurrentWorkTitle(),
    this.getCurrentRawVideoPath()
  );
  this.showNotice('视频已保存,可通过其他应用直接访问');
  this.currentSharedUri = uri;  // 保存 URI 供后续使用
}

8.3 兼容性考虑

问题 建议
旧设备不支持共享目录 保留 DocumentViewPicker 作为 fallback
共享目录路径版本差异 使用 canIUse('SystemCapability.FileManagement.CoreFileKit.SharedDir') 检测
文件大小限制 共享目录通常有配额限制,建议单文件不超过 500MB
并发访问冲突 写入共享文件后,其他应用可能立即访问,确保写入完成再暴露 URI
文件清理策略 应用卸载时共享目录自动清理,无需手动管理
// 能力检测示例
private async saveVideoWithFallback(): Promise<void> {
  if (canIUse('SystemCapability.FileManagement.CoreFileKit.SharedDir')) {
    // 使用 Core File Kit 共享目录(新流程)
    await this.saveCurrentVideoShared();
  } else {
    // 降级到传统 DocumentViewPicker(兼容旧设备)
    await this.saveCurrentVideoLegacy();
  }
}

九、与项目架构的融合

9.1 在 HarmonyFeaturesPage 中的展示

在项目的 HarmonyFeaturesPage.ets 中,"文件跨应用共享"功能卡可以展示 Core File Kit 的这一能力:

┌──────────────────────────────────────────┐
│  📂 文件跨应用共享                        │
│                                           │
│  Core File Kit 沙箱目录全局共享             │
│  保存到共享目录后,其他应用可直接访问        │
│                                           │
│  [保存作品到共享目录]                       │
│                                           │
│  共享状态:5 个文件可被其他应用访问          │
│  最近共享:kid_animation.mp4               │
│                                           │
│  已连接应用:微信 | WPS | 相册              │
└──────────────────────────────────────────┘

9.2 文件生命周期管理

共享目录中的文件虽然系统级可见,但其生命周期仍与应用绑定——应用卸载时,共享目录随沙箱一起删除。因此,在应用内部需要做好文件生命周期的管理:

class SharedFileManager {
  private sharedDir: string;

  constructor(context: common.UIAbilityContext) {
    this.sharedDir = CoreFileKitHelper.getSharedDir(context);
  }

  // 清理过期共享文件(例如:保留最近 7 天的文件)
  cleanExpiredFiles(maxAgeDays: number = 7): void {
    const now = Date.now();
    const maxAge = maxAgeDays * 24 * 60 * 60 * 1000;
    const files = fileIo.listFileSync(this.sharedDir);

    for (const file of files) {
      const filePath = `${this.sharedDir}/${file.name}`;
      const stat = fileIo.statSync(filePath);
      if (now - stat.mtime > maxAge) {
        fileIo.deleteFileSync(filePath);
        console.info(`已清理过期共享文件: ${file.name}`);
      }
    }
  }

  // 获取当前共享文件列表
  getSharedFiles(): Array<{ name: string, size: number, uri: string }> {
    const files = fileIo.listFileSync(this.sharedDir);
    return files.map(f => ({
      name: f.name,
      size: f.size,
      uri: fileUri.getUriFromPath(`${this.sharedDir}/${f.name}`)
    }));
  }
}

十、安全最佳实践

10.1 敏感数据不放入共享目录

共享目录中的文件对所有应用可见,因此绝对不要将敏感数据放入共享目录

// ❌ 错误:用户个人信息放入共享目录
const userProfilePath = `${sharedDir}/user_profile.json`;
fileIo.writeSync(file.fd, JSON.stringify({
  phone: userPhone,       // 敏感信息泄露
  address: userAddress    // 敏感信息泄露
}));

// ✅ 正确:仅将导出用的媒体文件放入共享目录
const exportVideoPath = `${sharedDir}/kid_animation.mp4`;
fileIo.writeSync(file.fd, videoBuffer);  // 媒体文件,无敏感信息

10.2 及时清理不再需要的共享文件

应用应当定期清理共享目录中的文件,避免长期暴露:

aboutToDisappear(): void {
  // 页面离开时,清理不再需要的共享文件
  const sharedFileManager = new SharedFileManager(getContext());
  sharedFileManager.cleanExpiredFiles(1);  // 保留 1 天的文件
}

10.3 文件完整性校验

当其他应用读取共享文件时,建议通过校验和(Checksum)验证文件完整性:

// 写入时计算哈希
import { hash } from '@kit.UniversalKit';

const contentHash = await hash.createHash(content);
// 将哈希值写入文件元数据或单独存储

// 读取时验证
const readHash = await hash.createHash(readContent);
if (readHash !== contentHash) {
  console.error('文件已被篡改或损坏');
}

10.4 安全检查清单

检查项 说明 状态
不存放敏感数据 个人身份信息、密码、Token 等不得放入共享目录
及时清理 用完即删,设置自动清理策略
文件类型限制 仅开放必要的文件类型(如视频/图片)
URI 最小暴露 仅在需要时传递 URI,用完即弃
权限检测 使用 canIUse 检测共享目录支持情况
写入完成后暴露 确保文件写入完成后再将 URI 传递给其他应用

总结

Core File Kit 的沙箱目录全局共享是 HarmonyOS 7 在文件系统层面的一次重要创新。它没有打破沙箱的安全基础,而是通过引入"共享区域"的巧妙设计,在安全与便利之间找到了精妙的平衡点。

知识点 说明
核心思想 在沙箱中划出共享区域,该区域文件系统级可见,无需导出导入
与传统方案对比 省去 DocumentViewPicker 和文件拷贝,文件写入即共享
fileUri 统一管理 file://docs/... 格式 URI 全局可解析,路径格式标准化
权限模型 基于 URI 授权的细粒度访问,无需全局存储权限
API 概览 createFile、openFile、getFileInfo、deleteFile、listFile
项目升级路径 三步渐进:新增共享路径 → 双路径写入 → 完全迁移
应用场景 跨应用文件共享、Content Provider、多设备 URI 传递
安全实践 敏感数据不放共享目录、及时清理、完整性校验

对于"画伴梦工厂"项目而言,Core File Kit 带来的直接收益是:用户创作动画视频后,不需要经过"保存→选择位置→等待拷贝→切换到其他应用→找到文件"的五步流程,而是直接在其他应用中打开该视频。步骤从五步变为一步,体验实现了质的飞跃。

从第 2.9 篇的 DocumentViewPicker 到本篇的 Core File Kit 沙箱共享,本质上是"文件拷贝式共享"向"文件引用式共享"的转变——不再复制数据,而是共享位置。


参考源码

本文内容基于 HarmonyOS 7(API 26)Core File Kit 开发文档:

  • @kit.CoreFileKitfileIo 文件读写、fileUri 路径管理
  • picker.DocumentViewPicker — 传统文件选择器(第 2.9 篇)
  • products/default/src/main/ets/services/VideoExportService.ets — 项目视频导出服务

相关系列文章:

  • 第 2.9 篇:视频导出与本地保存——DocumentViewPicker(传统方案的基础对比)
  • 第 7.14 篇:碰一碰精准分享(共享 URI 跨设备传递的应用场景)
  • 第 7.15 篇:分布式软总线 2.0(共享 URI 在分布式场景中的传输基础)
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐