HarmonyOS APP《画伴梦工厂》开发第66篇-CoreFileKit——沙箱目录全局共享
第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 与 cacheDir 和 filesDir 的核心区别
| 维度 | 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.CoreFileKit—fileIo文件读写、fileUri路径管理picker.DocumentViewPicker — 传统文件选择器(第 2.9 篇)products/default/src/main/ets/services/VideoExportService.ets— 项目视频导出服务
相关系列文章:
- 第 2.9 篇:视频导出与本地保存——DocumentViewPicker(传统方案的基础对比)
- 第 7.14 篇:碰一碰精准分享(共享 URI 跨设备传递的应用场景)
- 第 7.15 篇:分布式软总线 2.0(共享 URI 在分布式场景中的传输基础)
更多推荐



所有评论(0)