HarmonyOS分布式文件开发小技巧
【摘要】 文件是最常见的数据载体:照片、文档、视频、音频…当这些文件能在手机、平板、电脑间自由流转,多设备协同才真正有了"烟火气"。 一、背景与动机:为什么需要分布式文件? 1.1 传统文件共享的痛点场景1:照片分享手机拍了照片,想在电视上展示传统方案:微信传输、数据线拷贝、网盘中转问题:操作繁琐、速度慢、隐私风险场景2:文档编辑在平板上编辑文档,回办公室想在电脑继续传统方案:邮件发送、网盘同步、U盘...
文件是最常见的数据载体:照片、文档、视频、音频…当这些文件能在手机、平板、电脑间自由流转,多设备协同才真正有了"烟火气"。
一、背景与动机:为什么需要分布式文件?
1.1 传统文件共享的痛点
场景1:照片分享
- 手机拍了照片,想在电视上展示
- 传统方案:微信传输、数据线拷贝、网盘中转
- 问题:操作繁琐、速度慢、隐私风险
场景2:文档编辑
- 在平板上编辑文档,回办公室想在电脑继续
- 传统方案:邮件发送、网盘同步、U盘拷贝
- 问题:版本混乱、忘记同步、覆盖冲突
场景3:视频观看
- 下载的电影在手机上,想在智慧屏播放
- 传统方案:重新下载、局域网共享、投屏
- 问题:流量浪费、配置复杂、格式限制
1.2 分布式文件的设计目标
HarmonyOS分布式文件服务的设计目标:

二、核心原理:分布式文件系统架构
2.1 虚拟文件系统
分布式文件系统为应用提供统一的文件视图:
应用视角:
/data/user/0/com.example.app/
├── local/ # 本地文件(仅本设备可见)
├── distributed/ # 分布式文件(跨设备可见)
│ ├── device_A/ # 设备A的文件
│ ├── device_B/ # 设备B的文件
│ └── shared/ # 共享文件
└── cache/ # 缓存文件
2.2 文件访问流程
sequenceDiagram
participant App as 应用
participant VFS as 虚拟文件系统
participant Cache as 本地缓存
participant Remote as 远端设备
App->>VFS: open('/distributed/photo.jpg')
VFS->>Cache: 检查本地缓存
alt 缓存命中
Cache-->>VFS: 返回缓存文件
VFS-->>App: 文件句柄
else 缓存未命中
VFS->>Remote: 请求文件
Remote-->>VFS: 传输文件数据
VFS->>Cache: 写入缓存
VFS-->>App: 文件句柄
end
App->>VFS: read()
VFS-->>App: 文件内容
classDef primary fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
classDef warning fill:#F5A623,stroke:#C17D10,stroke-width:2px,color:#fff
classDef info fill:#7ED321,stroke:#5BA318,stroke-width:2px,color:#fff
class App primary
class VFS,Cache warning
class Remote info
2.3 文件传输策略
按需传输:
- 只有实际访问的文件才传输
- 避免全量同步浪费带宽
增量传输:
- 文件修改时,只传输变更部分
- 适用于大文件的频繁修改
预取策略:
- 根据访问模式预测,提前传输可能需要的文件
- 提升用户体验
2.4 文件元数据
每个分布式文件都携带元数据:
interface FileMetadata {
// 基本信息
name: string; // 文件名
size: number; // 文件大小
type: string; // MIME类型
extension: string; // 扩展名
// 位置信息
deviceId: string; // 来源设备ID
path: string; // 在来源设备的路径
// 时间信息
createTime: number; // 创建时间
modifyTime: number; // 修改时间
accessTime: number; // 访问时间
// 校验信息
hash: string; // 文件哈希值
version: number; // 版本号
// 权限信息
permissions: FilePermission;
}
interface FilePermission {
readable: boolean;
writable: boolean;
shareable: boolean;
deletable: boolean;
}
三、代码实战:实现分布式文件共享
3.1 基础示例:文件共享与访问
import fileShare from '@ohos.file.fileShare';
import fs from '@ohos.file.fs';
import { BusinessError } from '@ohos.base';
/**
* 分布式文件管理器
* 提供文件的共享、访问、传输能力
*/
export class DistributedFileManager {
private fileShareManager: fileShare.FileShareManager | null = null;
/**
* 初始化文件共享服务
*/
async init(): Promise<void> {
try {
// 创建文件共享管理器
this.fileShareManager = fileShare.createFileShareManager();
console.info('[DistributedFile] 文件共享服务初始化成功');
} catch (error) {
const err = error as BusinessError;
console.error(`[DistributedFile] 初始化失败: ${err.code} - ${err.message}`);
throw error;
}
}
/**
* 共享文件
* 将本地文件共享给其他设备
* @param localPath 本地文件路径
* @param shareName 共享名称(其他设备看到的名称)
*/
async shareFile(localPath: string, shareName?: string): Promise<string> {
if (!this.fileShareManager) {
throw new Error('文件共享服务未初始化');
}
try {
// 检查文件是否存在
const stat = fs.statSync(localPath);
if (!stat.isFile()) {
throw new Error('指定路径不是文件');
}
// 共享配置
const shareConfig: fileShare.ShareConfig = {
path: localPath,
name: shareName || this.extractFileName(localPath),
mode: fileShare.ShareMode.READ_WRITE, // 读写模式
autoSync: true // 自动同步
};
// 执行共享
const shareResult = await this.fileShareManager.share(shareConfig);
console.info(`[DistributedFile] 文件共享成功: ${localPath} -> ${shareResult.shareId}`);
console.info(` 共享名称: ${shareConfig.name}`);
console.info(` 文件大小: ${stat.size} bytes`);
return shareResult.shareId;
} catch (error) {
console.error(`[DistributedFile] 共享失败: ${error}`);
throw error;
}
}
/**
* 访问远端文件
* @param deviceId 远端设备ID
* @param shareId 共享ID
* @returns 本地访问路径
*/
async accessRemoteFile(deviceId: string, shareId: string): Promise<string> {
if (!this.fileShareManager) {
throw new Error('文件共享服务未初始化');
}
try {
// 访问配置
const accessConfig: fileShare.AccessConfig = {
deviceId: deviceId,
shareId: shareId,
cacheMode: fileShare.CacheMode.AUTO // 自动缓存
};
// 访问远端文件
const accessResult = await this.fileShareManager.access(accessConfig);
console.info(`[DistributedFile] 远端文件访问成功`);
console.info(` 设备: ${deviceId}`);
console.info(` 本地路径: ${accessResult.localPath}`);
console.info(` 文件大小: ${accessResult.size} bytes`);
return accessResult.localPath;
} catch (error) {
console.error(`[DistributedFile] 访问失败: ${error}`);
throw error;
}
}
/**
* 列出可访问的远端文件
* @param deviceId 远端设备ID
*/
async listRemoteFiles(deviceId: string): Promise<RemoteFileInfo[]> {
if (!this.fileShareManager) {
throw new Error('文件共享服务未初始化');
}
try {
const files = await this.fileShareManager.listSharedFiles(deviceId);
console.info(`[DistributedFile] 设备 ${deviceId} 共享了 ${files.length} 个文件:`);
for (const file of files) {
console.info(` - ${file.name} (${this.formatSize(file.size)})`);
}
return files;
} catch (error) {
console.error(`[DistributedFile] 列出文件失败: ${error}`);
throw error;
}
}
/**
* 取消文件共享
* @param shareId 共享ID
*/
async unshareFile(shareId: string): Promise<void> {
if (!this.fileShareManager) {
throw new Error('文件共享服务未初始化');
}
try {
await this.fileShareManager.unshare(shareId);
console.info(`[DistributedFile] 文件共享已取消: ${shareId}`);
} catch (error) {
console.error(`[DistributedFile] 取消共享失败: ${error}`);
throw error;
}
}
/**
* 从路径提取文件名
*/
private extractFileName(path: string): string {
const parts = path.split('/');
return parts[parts.length - 1];
}
/**
* 格式化文件大小
*/
private formatSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
}
interface RemoteFileInfo {
shareId: string;
name: string;
size: number;
type: string;
modifyTime: number;
}
3.2 进阶示例:文件传输与监控
import fileShare from '@ohos.file.fileShare';
import fs from '@ohos.file.fs';
/**
* 文件传输管理器
* 提供传输进度监控、断点续传能力
*/
export class FileTransferManager {
private fileShareManager: fileShare.FileShareManager | null = null;
private transferCallbacks: Map<string, TransferCallback> = new Map();
/**
* 初始化
*/
async init(): Promise<void> {
this.fileShareManager = fileShare.createFileShareManager();
this.setupTransferListener();
}
/**
* 下载远端文件(带进度)
* @param deviceId 远端设备ID
* @param shareId 共享ID
* @param localPath 本地保存路径
* @param onProgress 进度回调
*/
async downloadFile(deviceId: string, shareId: string, localPath: string,
onProgress?: (progress: TransferProgress) => void): Promise<void> {
if (!this.fileShareManager) {
throw new Error('文件共享服务未初始化');
}
try {
// 获取远端文件信息
const accessResult = await this.fileShareManager.access({
deviceId,
shareId,
cacheMode: fileShare.CacheMode.MANUAL
});
const remotePath = accessResult.localPath;
const fileSize = accessResult.size;
// 注册进度回调
const transferId = `download_${Date.now()}`;
if (onProgress) {
this.transferCallbacks.set(transferId, { onProgress });
}
// 执行下载
await this.copyFileWithProgress(remotePath, localPath, fileSize, (copied, total) => {
if (onProgress) {
onProgress({
transferId,
deviceId,
fileName: accessResult.name,
transferred: copied,
total: total,
speed: 0, // 实际应计算速度
percentage: (copied / total) * 100
});
}
});
// 清理回调
this.transferCallbacks.delete(transferId);
console.info(`[FileTransfer] 文件下载完成: ${localPath}`);
} catch (error) {
console.error(`[FileTransfer] 下载失败: ${error}`);
throw error;
}
}
/**
* 带进度的文件复制
*/
private async copyFileWithProgress(srcPath: string, dstPath: string,
totalSize: number,
onProgress: (copied: number, total: number) => void): Promise<void> {
const CHUNK_SIZE = 64 * 1024; // 64KB每块
// 打开源文件
const srcFile = fs.openSync(srcPath, fs.OpenMode.READ_ONLY);
// 创建目标文件
const dstFile = fs.openSync(dstPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
try {
let copied = 0;
const buffer = new ArrayBuffer(CHUNK_SIZE);
while (copied < totalSize) {
// 读取一块数据
const readLen = fs.readSync(srcFile.fd, buffer);
if (readLen === 0) break;
// 写入目标文件
fs.writeSync(dstFile.fd, buffer, { length: readLen });
copied += readLen;
// 报告进度
onProgress(copied, totalSize);
// 让出执行权,避免阻塞
await new Promise(resolve => setTimeout(resolve, 0));
}
} finally {
// 关闭文件
fs.closeSync(srcFile);
fs.closeSync(dstFile);
}
}
/**
* 设置传输监听
*/
private setupTransferListener(): void {
if (!this.fileShareManager) return;
// 监听传输状态变化
this.fileShareManager.on('transferStateChange', (event: fileShare.TransferEvent) => {
const callback = this.transferCallbacks.get(event.transferId);
if (callback && callback.onProgress) {
callback.onProgress({
transferId: event.transferId,
deviceId: event.deviceId,
fileName: event.fileName,
transferred: event.transferred,
total: event.total,
speed: event.speed,
percentage: (event.transferred / event.total) * 100
});
}
});
}
/**
* 取消传输
*/
async cancelTransfer(transferId: string): Promise<void> {
if (!this.fileShareManager) return;
await this.fileShareManager.cancelTransfer(transferId);
this.transferCallbacks.delete(transferId);
console.info(`[FileTransfer] 传输已取消: ${transferId}`);
}
}
interface TransferCallback {
onProgress?: (progress: TransferProgress) => void;
}
interface TransferProgress {
transferId: string;
deviceId: string;
fileName: string;
transferred: number; // 已传输字节数
total: number; // 总字节数
speed: number; // 传输速度(bytes/s)
percentage: number; // 完成百分比
}
3.3 高级示例:文件同步与版本管理
import fileShare from '@ohos.file.fileShare';
import fs from '@ohos.file.fs';
import crypto from '@ohos.security.crypto';
/**
* 文件版本信息
*/
interface FileVersion {
version: number;
hash: string;
timestamp: number;
deviceId: string;
size: number;
}
/**
* 文件同步状态
*/
enum SyncStatus {
SYNCED = 'synced', // 已同步
PENDING = 'pending', // 待同步
CONFLICT = 'conflict', // 冲突
SYNCING = 'syncing' // 同步中
}
/**
* 高级文件同步管理器
* 提供版本控制、冲突检测、增量同步能力
*/
export class AdvancedFileSync {
private fileShareManager: fileShare.FileShareManager | null = null;
private versionCache: Map<string, FileVersion> = new Map();
/**
* 初始化
*/
async init(): Promise<void> {
this.fileShareManager = fileShare.createFileShareManager();
await this.loadVersionCache();
}
/**
* 同步文件(带版本控制)
* @param localPath 本地文件路径
* @param remoteDeviceId 远端设备ID
* @param remoteShareId 远端共享ID
*/
async syncFile(localPath: string, remoteDeviceId: string, remoteShareId: string): Promise<SyncResult> {
const result: SyncResult = {
status: SyncStatus.SYNCED,
localPath,
message: ''
};
try {
// 计算本地文件哈希
const localHash = await this.calculateFileHash(localPath);
const localStat = fs.statSync(localPath);
// 获取本地版本信息
const localVersion = this.versionCache.get(localPath);
// 获取远端文件信息
const remoteInfo = await this.fileShareManager!.getShareInfo(remoteShareId, remoteDeviceId);
// 检查是否需要同步
if (localVersion && localVersion.hash === remoteInfo.hash) {
// 哈希相同,无需同步
result.message = '文件已是最新,无需同步';
return result;
}
// 检测冲突
if (localVersion && localVersion.timestamp < remoteInfo.modifyTime) {
// 远端更新,拉取远端
result.status = SyncStatus.SYNCING;
await this.pullRemoteFile(localPath, remoteDeviceId, remoteShareId);
result.message = '已从远端拉取最新文件';
} else if (localVersion && localVersion.timestamp > remoteInfo.modifyTime) {
// 本地更新,推送到远端
result.status = SyncStatus.SYNCING;
await this.pushLocalFile(localPath, remoteDeviceId, remoteShareId);
result.message = '已将本地文件推送到远端';
} else {
// 并发修改,冲突
result.status = SyncStatus.CONFLICT;
result.message = '检测到并发修改,需要手动解决冲突';
result.conflict = {
localHash,
remoteHash: remoteInfo.hash,
localTime: localVersion?.timestamp || 0,
remoteTime: remoteInfo.modifyTime
};
}
// 更新版本缓存
this.updateVersionCache(localPath, {
version: (localVersion?.version || 0) + 1,
hash: localHash,
timestamp: Date.now(),
deviceId: await this.getDeviceId(),
size: localStat.size
});
return result;
} catch (error) {
result.status = SyncStatus.PENDING;
result.message = `同步失败: ${error}`;
return result;
}
}
/**
* 增量同步
* 只同步文件变更部分
*/
async incrementalSync(localPath: string, remoteDeviceId: string, remoteShareId: string): Promise<void> {
// 获取本地文件版本
const localVersion = this.versionCache.get(localPath);
if (!localVersion) {
// 没有版本信息,执行全量同步
await this.syncFile(localPath, remoteDeviceId, remoteShareId);
return;
}
// 获取远端文件版本
const remoteInfo = await this.fileShareManager!.getShareInfo(remoteShareId, remoteDeviceId);
if (remoteInfo.modifyTime <= localVersion.timestamp) {
// 远端未更新
console.info('[FileSync] 远端文件未更新,无需同步');
return;
}
// 执行增量同步
// 这里简化处理,实际应根据文件类型实现增量算法
// 例如:文本文件可以diff,二进制文件可以分块比较
console.info('[FileSync] 执行增量同步');
await this.pullRemoteFile(localPath, remoteDeviceId, remoteShareId);
}
/**
* 批量同步目录
*/
async syncDirectory(localDir: string, remoteDeviceId: string): Promise<BatchSyncResult> {
const result: BatchSyncResult = {
total: 0,
synced: 0,
failed: 0,
conflicts: 0,
details: []
};
try {
// 列出本地文件
const localFiles = this.listFilesRecursively(localDir);
// 列出远端文件
const remoteFiles = await this.fileShareManager!.listSharedFiles(remoteDeviceId);
result.total = localFiles.length + remoteFiles.length;
// 同步本地文件
for (const localFile of localFiles) {
try {
const relativePath = localFile.substring(localDir.length);
const remoteFile = remoteFiles.find(r => r.name === relativePath);
if (remoteFile) {
// 两端都有,执行同步
const syncResult = await this.syncFile(localFile, remoteDeviceId, remoteFile.shareId);
if (syncResult.status === SyncStatus.SYNCED) {
result.synced++;
} else if (syncResult.status === SyncStatus.CONFLICT) {
result.conflicts++;
}
result.details.push({
file: localFile,
status: syncResult.status,
message: syncResult.message
});
} else {
// 仅本地有,推送到远端
await this.shareFile(localFile);
result.synced++;
}
} catch (error) {
result.failed++;
result.details.push({
file: localFile,
status: SyncStatus.PENDING,
message: `同步失败: ${error}`
});
}
}
console.info(`[FileSync] 批量同步完成: 总计 ${result.total}, 成功 ${result.synced}, 失败 ${result.failed}, 冲突 ${result.conflicts}`);
return result;
} catch (error) {
console.error(`[FileSync] 批量同步失败: ${error}`);
throw error;
}
}
/**
* 计算文件哈希
*/
private async calculateFileHash(filePath: string): Promise<string> {
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
try {
const stat = fs.statSync(filePath);
const buffer = new ArrayBuffer(stat.size);
fs.readSync(file.fd, buffer);
// 使用MD5计算哈希
const md = crypto.createMd('MD5');
md.update(buffer);
const hash = md.digest();
// 转换为十六进制字符串
return this.bufferToHex(hash);
} finally {
fs.closeSync(file);
}
}
/**
* ArrayBuffer转十六进制字符串
*/
private bufferToHex(buffer: ArrayBuffer): string {
const view = new Uint8Array(buffer);
let hex = '';
for (let i = 0; i < view.length; i++) {
hex += view[i].toString(16).padStart(2, '0');
}
return hex;
}
/**
* 递归列出目录下所有文件
*/
private listFilesRecursively(dir: string): string[] {
const files: string[] = [];
const list = fs.listFileSync(dir);
for (const name of list) {
const path = `${dir}/${name}`;
const stat = fs.statSync(path);
if (stat.isFile()) {
files.push(path);
} else if (stat.isDirectory()) {
files.push(...this.listFilesRecursively(path));
}
}
return files;
}
/**
* 拉取远端文件
*/
private async pullRemoteFile(localPath: string, deviceId: string, shareId: string): Promise<void> {
const accessResult = await this.fileShareManager!.access({
deviceId,
shareId,
cacheMode: fileShare.CacheMode.AUTO
});
// 复制文件
fs.copyFileSync(accessResult.localPath, localPath);
}
/**
* 推送本地文件
*/
private async pushLocalFile(localPath: string, deviceId: string, shareId: string): Promise<void> {
// 实际项目中应实现推送逻辑
console.info(`[FileSync] 推送文件到设备 ${deviceId}: ${localPath}`);
}
/**
* 共享文件
*/
private async shareFile(localPath: string): Promise<string> {
const result = await this.fileShareManager!.share({
path: localPath,
name: this.extractFileName(localPath),
mode: fileShare.ShareMode.READ_WRITE,
autoSync: true
});
return result.shareId;
}
/**
* 加载版本缓存
*/
private async loadVersionCache(): Promise<void> {
// 从本地存储加载版本信息
// 这里简化处理
}
/**
* 更新版本缓存
*/
private updateVersionCache(path: string, version: FileVersion): void {
this.versionCache.set(path, version);
// 持久化到本地存储
}
private extractFileName(path: string): string {
const parts = path.split('/');
return parts[parts.length - 1];
}
private async getDeviceId(): Promise<string> {
return 'device_' + Date.now();
}
}
interface SyncResult {
status: SyncStatus;
localPath: string;
message: string;
conflict?: {
localHash: string;
remoteHash: string;
localTime: number;
remoteTime: number;
};
}
interface BatchSyncResult {
total: number;
synced: number;
failed: number;
conflicts: number;
details: Array<{
file: string;
status: SyncStatus;
message: string;
}>;
}
四、踩坑与注意事项
4.1 大文件传输超时
问题:大文件传输时,可能因超时而中断。
解决方案:分块传输 + 断点续传
/**
* 断点续传管理器
*/
export class ResumableTransfer {
private readonly CHUNK_SIZE = 1024 * 1024; // 1MB每块
private progressFile: string = '/data/transfer_progress.json';
/**
* 断点续传下载
*/
async resumableDownload(srcPath: string, dstPath: string): Promise<void> {
// 加载进度
const progress = await this.loadProgress(dstPath);
const srcFile = fs.openSync(srcPath, fs.OpenMode.READ_ONLY);
const dstFile = fs.openSync(dstPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
try {
const stat = fs.statSync(srcPath);
const totalSize = stat.size;
// 跳过已传输的部分
if (progress.transferred > 0) {
fs.lseekSync(dstFile.fd, progress.transferred, fs.WhenceType.SEEK_SET);
console.info(`[ResumableTransfer] 从 ${progress.transferred} 字节处恢复传输`);
}
const buffer = new ArrayBuffer(this.CHUNK_SIZE);
let offset = progress.transferred;
while (offset < totalSize) {
try {
// 定位读取位置
fs.lseekSync(srcFile.fd, offset, fs.WhenceType.SEEK_SET);
// 读取一块
const readLen = fs.readSync(srcFile.fd, buffer);
if (readLen === 0) break;
// 写入
fs.writeSync(dstFile.fd, buffer, { length: readLen });
offset += readLen;
// 保存进度
await this.saveProgress(dstPath, { transferred: offset, total: totalSize });
} catch (error) {
// 传输中断,保存进度后退出
console.error(`[ResumableTransfer] 传输中断: ${error}`);
await this.saveProgress(dstPath, { transferred: offset, total: totalSize });
throw error;
}
}
// 传输完成,清除进度
await this.clearProgress(dstPath);
} finally {
fs.closeSync(srcFile);
fs.closeSync(dstFile);
}
}
/**
* 加载传输进度
*/
private async loadProgress(filePath: string): Promise<{ transferred: number; total: number }> {
try {
const data = fs.readTextSync(this.progressFile);
const all = JSON.parse(data);
return all[filePath] || { transferred: 0, total: 0 };
} catch {
return { transferred: 0, total: 0 };
}
}
/**
* 保存传输进度
*/
private async saveProgress(filePath: string, progress: { transferred: number; total: number }): Promise<void> {
let all: Record<string, any> = {};
try {
const data = fs.readTextSync(this.progressFile);
all = JSON.parse(data);
} catch {
// 文件不存在
}
all[filePath] = progress;
fs.writeTextSync(this.progressFile, JSON.stringify(all));
}
/**
* 清除传输进度
*/
private async clearProgress(filePath: string): Promise<void> {
let all: Record<string, any> = {};
try {
const data = fs.readTextSync(this.progressFile);
all = JSON.parse(data);
delete all[filePath];
fs.writeTextSync(this.progressFile, JSON.stringify(all));
} catch {
// 忽略
}
}
}
4.2 文件权限问题
问题:访问远端文件时,可能因权限不足而失败。
解决方案:权限检查 + 降级处理
/**
* 文件权限检查器
*/
export class FilePermissionChecker {
/**
* 检查文件访问权限
*/
async checkAccess(path: string, mode: 'r' | 'w' | 'rw'): Promise<boolean> {
try {
const stat = fs.statSync(path);
// 检查读权限
if (mode.includes('r')) {
fs.accessSync(path, fs.AccessModeType.READ);
}
// 检查写权限
if (mode.includes('w')) {
fs.accessSync(path, fs.AccessModeType.WRITE);
}
return true;
} catch (error) {
console.warn(`[Permission] 文件访问权限不足: ${path}, 模式: ${mode}`);
return false;
}
}
/**
* 安全打开文件
* 失败时返回null而不是抛异常
*/
async safeOpen(path: string, mode: number): Promise<fs.File | null> {
const hasAccess = await this.checkAccess(path, mode & fs.OpenMode.READ_ONLY ? 'r' : 'rw');
if (!hasAccess) {
return null;
}
try {
return fs.openSync(path, mode);
} catch {
return null;
}
}
}
4.3 缓存空间管理
问题:大量文件缓存占用过多空间。
解决方案:LRU缓存 + 空间限制
/**
* 文件缓存管理器
*/
export class FileCacheManager {
private maxCacheSize: number = 500 * 1024 * 1024; // 500MB
private cacheDir: string = '/data/file_cache';
private cacheIndex: Map<string, CacheEntry> = new Map();
/**
* 初始化缓存
*/
async init(): Promise<void> {
await this.loadCacheIndex();
await this.cleanupCache();
}
/**
* 获取缓存文件
*/
async getCache(key: string): Promise<string | null> {
const entry = this.cacheIndex.get(key);
if (!entry) {
return null;
}
// 更新访问时间
entry.lastAccess = Date.now();
return entry.path;
}
/**
* 添加缓存
*/
async putCache(key: string, filePath: string): Promise<string> {
// 计算需要的空间
const stat = fs.statSync(filePath);
const neededSize = stat.size;
// 检查是否有足够空间
const currentSize = this.getCurrentCacheSize();
if (currentSize + neededSize > this.maxCacheSize) {
// 空间不足,清理旧缓存
await this.evictCache(neededSize);
}
// 复制文件到缓存目录
const cachePath = `${this.cacheDir}/${key}`;
fs.copyFileSync(filePath, cachePath);
// 更新索引
this.cacheIndex.set(key, {
path: cachePath,
size: neededSize,
createTime: Date.now(),
lastAccess: Date.now()
});
await this.saveCacheIndex();
return cachePath;
}
/**
* 清理缓存
* 使用LRU策略
*/
private async evictCache(neededSize: number): Promise<void> {
// 按访问时间排序
const entries = Array.from(this.cacheIndex.entries())
.sort((a, b) => a[1].lastAccess - b[1].lastAccess);
let freedSize = 0;
for (const [key, entry] of entries) {
// 删除缓存文件
try {
fs.unlinkSync(entry.path);
} catch {
// 忽略删除失败
}
// 从索引移除
this.cacheIndex.delete(key);
freedSize += entry.size;
if (freedSize >= neededSize) {
break;
}
}
console.info(`[CacheManager] 清理了 ${freedSize} 字节缓存空间`);
await this.saveCacheIndex();
}
/**
* 获取当前缓存大小
*/
private getCurrentCacheSize(): number {
let total = 0;
for (const entry of this.cacheIndex.values()) {
total += entry.size;
}
return total;
}
/**
* 加载缓存索引
*/
private async loadCacheIndex(): Promise<void> {
try {
const indexPath = `${this.cacheDir}/index.json`;
const data = fs.readTextSync(indexPath);
const obj = JSON.parse(data);
for (const [key, value] of Object.entries(obj)) {
this.cacheIndex.set(key, value as CacheEntry);
}
} catch {
// 索引不存在,忽略
}
}
/**
* 保存缓存索引
*/
private async saveCacheIndex(): Promise<void> {
const obj: Record<string, CacheEntry> = {};
for (const [key, value] of this.cacheIndex) {
obj[key] = value;
}
const indexPath = `${this.cacheDir}/index.json`;
fs.writeTextSync(indexPath, JSON.stringify(obj));
}
/**
* 清理无效缓存
*/
private async cleanupCache(): Promise<void> {
const toDelete: string[] = [];
for (const [key, entry] of this.cacheIndex) {
try {
fs.accessSync(entry.path);
} catch {
// 文件不存在,标记删除
toDelete.push(key);
}
}
for (const key of toDelete) {
this.cacheIndex.delete(key);
}
if (toDelete.length > 0) {
await this.saveCacheIndex();
console.info(`[CacheManager] 清理了 ${toDelete.length} 个无效缓存`);
}
}
}
interface CacheEntry {
path: string;
size: number;
createTime: number;
lastAccess: number;
}
五、HarmonyOS 6适配指南
5.1 API变更
5.1.1 文件共享API增强
// HarmonyOS 5.0
const result = await fileShareManager.share({
path: localPath,
name: fileName,
mode: fileShare.ShareMode.READ_WRITE
});
// HarmonyOS 6 - 增强的共享配置
import fileShare from '@kit.FileShare';
const shareConfig: fileShare.ShareConfig = {
path: localPath,
name: fileName,
mode: fileShare.ShareMode.READ_WRITE,
// HarmonyOS 6新增配置
autoSync: true,
// 访问控制
accessControl: {
maxAccessCount: 10, // 最大访问设备数
expireTime: 7 * 24 * 60 * 60 * 1000, // 过期时间(7天)
password: '123456' // 访问密码(可选)
},
// 传输配置
transferConfig: {
compress: true, // 启用压缩
encrypt: true, // 启用加密
chunkSize: 1024 * 1024, // 分块大小(1MB)
priority: 'high' // 传输优先级
},
// 元数据
metadata: {
category: 'document', // 文件分类
tags: ['work', 'important'], // 标签
description: '项目文档' // 描述
}
};
const result = await fileShareManager.share(shareConfig);
5.1.2 文件访问API增强
// HarmonyOS 6新增:流式访问
const accessConfig: fileShare.AccessConfig = {
deviceId: remoteDeviceId,
shareId: shareId,
// 访问模式
accessMode: fileShare.AccessMode.STREAM, // 流式访问
// 缓存配置
cacheConfig: {
enabled: true,
maxSize: 100 * 1024 * 1024, // 最大缓存100MB
policy: 'lru' // LRU策略
},
// 预取配置
prefetch: {
enabled: true,
relatedFiles: ['file2.jpg', 'file3.jpg'] // 预取相关文件
}
};
const stream = await fileShareManager.accessStream(accessConfig);
// 流式读取
const buffer = new ArrayBuffer(1024);
const readLen = await stream.read(buffer);
5.2 行为变更
5.2.1 自动压缩
// HarmonyOS 6:自动压缩传输
// 对于可压缩文件类型(文本、图片等),自动启用压缩
const shareConfig: fileShare.ShareConfig = {
path: localPath,
name: fileName,
mode: fileShare.ShareMode.READ_WRITE,
// 自动压缩配置
compress: {
enabled: true,
algorithm: 'zstd', // 压缩算法
level: 3, // 压缩级别(1-9)
threshold: 10 * 1024 // 超过10KB才压缩
}
};
5.3 性能优化
/**
* HarmonyOS 6文件共享性能优化
*/
export class HarmonyOS6FileOptimization {
/**
* 推荐配置
*/
getOptimizedShareConfig(): fileShare.ShareConfig {
return {
path: '',
name: '',
mode: fileShare.ShareMode.READ_WRITE,
autoSync: true,
// 传输优化
transferConfig: {
compress: true,
encrypt: false, // 非敏感数据不加密,提升性能
chunkSize: 2 * 1024 * 1024, // 2MB分块
priority: 'normal',
// 并行传输
parallel: {
enabled: true,
maxConnections: 4 // 最多4个并行连接
}
},
// 缓存优化
cacheConfig: {
enabled: true,
maxSize: 200 * 1024 * 1024, // 200MB缓存
policy: 'lru',
prefetch: true // 启用预取
}
};
}
}
六、总结
分布式文件是HarmonyOS多设备协同的重要基础设施。通过本文的深度解析,我们掌握了:
核心要点:
- 架构理解:虚拟文件系统、统一文件视图、按需传输
- API使用:文件共享、远端访问、传输监控、版本管理
- 传输优化:断点续传、分块传输、压缩传输
- 缓存管理:LRU策略、空间限制、预取策略
- 避坑指南:大文件超时、权限问题、缓存空间
- 版本适配:HarmonyOS 6的API增强、性能优化
最佳实践:
- 小文件:直接共享,自动同步
- 大文件:分块传输,断点续传
- 频繁访问:启用缓存,LRU管理
- 敏感文件:启用加密,设置访问密码
应用场景:
- 照片共享:手机拍照,电视展示
- 文档协同:平板编辑,电脑查看
- 媒体播放:手机下载,智慧屏播放
- 数据备份:重要文件跨设备备份
掌握分布式文件,让你的应用真正实现"文件随设备而动,设备随用户而在"的分布式愿景。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)