分布式文件服务:跨设备文件访问与传输(62)
·
在鸿蒙(HarmonyOS)生态中,分布式文件系统(DFS)打破了设备间的物理存储边界,使得同一应用在不同设备上的沙箱目录能够像本地文件夹一样被直接访问和同步。
以下是实现跨设备文件访问与传输的核心原理及代码:
一、 核心前置准备
跨设备文件流转依赖于分布式软总线的设备发现能力,且需要满足以下环境与权限条件:
- 网络与账号:两台设备必须登录同一个鸿蒙系统账号,并开启蓝牙和 Wi-Fi 功能(无需进行传统的蓝牙配对,也无需连接同一局域网)。
- 权限申请:应用必须动态申请
ohos.permission.DISTRIBUTED_DATASYNC权限,以获取分布式数据同步的授权。
二、 跨设备文件传输代码实战
1. 动态申请分布式同步权限
在传输文件前,必须先获取用户的授权:
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
let atManager = abilityAccessCtrl.createAtManager();
try {
// 以动态弹窗的方式向用户申请分布式数据同步权限
atManager.requestPermissionsFromUser(context, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((result) => {
console.info(`权限申请结果: ${JSON.stringify(result)}`);
}).catch((err: BusinessError) => {
console.error(`权限申请失败。Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
console.error('捕获权限申请异常');
}
2. 设备 A:将文件写入分布式目录
将需要跨设备传输的文件从应用本地沙箱拷贝到 distributedFilesDir(分布式文件目录)中。系统会自动触发底层同步机制:
import { fileIo as fs } from '@kit.CoreFileKit';
import { fileUri } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
let context = getContext(this) as common.UIAbilityContext;
let pathDir: string = context.filesDir; // 本地沙箱目录
let distributedPathDir: string = context.distributedFilesDir; // 分布式目录
let srcFilePath: string = pathDir + '/src.txt';
let destFilePath: string = distributedPathDir + '/src.txt';
try {
let srcUri = fileUri.getUriFromPath(srcFilePath);
let destUri = fileUri.getUriFromPath(destFilePath);
// 将本地文件拷贝到分布式目录
fs.copy(srcUri, destUri).then(() => {
console.info('文件成功写入分布式目录,等待系统自动同步');
}).catch((err: BusinessError) => {
console.error('拷贝失败: ' + err.message);
});
} catch (error) {
console.error('文件操作异常');
}
3. 设备 B:发现设备并建立分布式连接
设备 B 需要通过 distributedDeviceManager 获取设备 A 的 networkId,并主动发起分布式文件系统建链(connectDfs):
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { fileIo as fs } from '@kit.CoreFileKit';
// 1. 获取可用设备列表并拿到目标设备的 networkId
let dmInstance = distributedDeviceManager.createDeviceManager('com.example.hap');
let deviceInfoList = dmInstance.getAvailableDeviceListSync();
let networkId = deviceInfoList[0].networkId;
// 2. 定义连接状态监听
let listeners: fs.DfsListeners = {
onStatus: (networkId: string, status: number): void => {
console.info(`分布式文件系统连接状态变更: ${status}`);
}
};
// 3. 发起跨设备文件系统建链
fs.connectDfs(networkId, listeners).then(() => {
console.info('成功连接到远端设备的分布式文件系统');
// 连接成功后,即可像读取本地文件一样读取远端文件
}).catch((err: BusinessError) => {
console.error('建链失败: ' + err.message);
});
4. 设备 B:读取远端文件或拷贝到本地
建链成功后,设备 B 可以直接通过本地的 distributedFilesDir 路径访问设备 A 的文件,或者将其拷贝到本地沙箱中:
import { buffer } from '@kit.ArkTS';
// 场景 A:直接读取远端文件内容
let remoteFilePath = context.distributedFilesDir + '/src.txt';
try {
let file = fs.openSync(remoteFilePath, fs.OpenMode.READ_ONLY);
let arrayBuffer = new ArrayBuffer(4096);
let num = fs.readSync(file.fd, arrayBuffer);
let buf = buffer.from(arrayBuffer, 0, num);
console.info('读取远端文件内容: ' + buf.toString());
fs.closeSync(file);
} catch (err) {
console.error('读取远端文件失败');
}
// 场景 B:将远端分布式文件拷贝到本地沙箱
let localDestPath = context.filesDir + '/dest.txt';
fs.copy(fileUri.getUriFromPath(remoteFilePath), fileUri.getUriFromPath(localDestPath)).then(() => {
console.info('跨设备文件拷贝到本地成功');
});
5. 断开分布式连接
文件传输或访问完成后,应及时释放连接资源:
fs.disconnectDfs(networkId).then(() => {
console.info('成功断开分布式文件系统连接');
}).catch((err: BusinessError) => {
console.error('断开连接失败: ' + err.message);
});
核心机制
- 无感挂载:鸿蒙通过
connectDfs将远端设备的分布式目录“挂载”到本地,开发者无需关心底层的网络协议和文件传输细节,直接调用标准的fs.open、fs.read、fs.copy即可完成跨设备操作。 - URI 驱动流转:在更高级的跨应用流转场景中,应用可以将本地文件转换为分布式 URI,通过剪贴板或拖拽传递给其他设备上的应用,接收端直接通过 URI 打开,实现“拖拽即发送”的秒传体验。
三、 端云协同:本地缓存与云端无缝同步
除了设备间的横向同步,鸿蒙还提供了纵向的“端云文件协同”能力。开发者可以在沙箱的 /data/storage/el2/cloud 目录下进行文件操作,系统会自动触发同步流程,将文件上传至云端或从云端拉取。
- 同步状态监听与主动触发:针对弱网、低电量或设备过热等异常场景,系统可能会延迟同步。开发者可通过
cloudSync.FileSync对象订阅同步进度,并手动触发或停止同步。
import { cloudSync } from '@kit.CoreFileKit';
// 场景:监听端云同步状态,并在必要时主动触发
let fileSync = new cloudSync.FileSync();
// 1. 注册同步进度监听
fileSync.on('progress', (pg: cloudSync.SyncProgress) => {
console.info(`当前同步状态: ${pg.state}, 错误码: ${pg.error}`);
});
// 2. 主动触发端云同步(当端侧与云侧数据不一致时)
try {
await fileSync.start();
console.info('端云同步任务已启动');
} catch (error) {
console.error('触发同步失败:', error);
}
四、 高级分布式特性:透明访问与冲突处理
在复杂的分布式组网环境下,文件系统提供了一系列高级特性来保障数据的一致性与访问的便捷性。
- 透明访问与 Close-to-Open 一致性:应用使用标准的
@ohos.file.fs接口即可访问远端文件。系统保证“一端写关闭后,另外一端可以读取到最新数据”,但不会主动进行文件数据传输和拷贝,如需保存到本地需主动调用拷贝接口。 - 自动冲突处理:当本地文件与远端文件命名相同时,系统会自动重命名远端同步过来的文件(后缀加
_conflict_dev及递增 ID),保持本地文件不变。若远端多个设备存在冲突,则按设备 ID 大小依次重命名。
// 场景:处理跨设备文件访问时的冲突与重命名
// 开发者在读取分布式目录时,需考虑同名文件被自动重命名的情况
let distributedDir = context.distributedFilesDir;
// 假设远端同步过来的文件名为 report_conflict_dev1.pdf
let remoteFile = fs.openSync(distributedDir + '/report_conflict_dev1.pdf', fs.OpenMode.READ_ONLY);
// 业务逻辑:读取远端文件内容并合并到本地...
五、 安全与异常处理:超时感知与权限管控
分布式网络环境具有不确定性,开发者必须在代码中做好防御性编程。
- 离线感知与超时处理:远端设备离线后,本端感知存在延迟(约 4 秒超时)。在此期间,部分文件可能依然可见但实际已无法访问,开发者必须捕获网络超时异常。
- 精细化权限管控:在跨设备共享文件时,系统会基于设备信任关系和应用权限进行验证。开发者可以通过
distributedfile.grantPermission等接口实现细粒度的读写权限控制。
// 场景:防御性读取远端文件(处理超时与设备离线)
async function safeReadRemoteFile(filePath: string) {
try {
let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
// 执行读取操作...
fs.closeSync(file);
} catch (err) {
// 捕获因设备离线或网络超时导致的异常
if (err.code === 13900042) { // 示例:网络超时错误码
console.warn('远端设备可能已离线或网络超时,请稍后重试');
} else {
console.error('文件读取失败:', err);
}
}
}
六、 企业级文件协同最佳实践
- 读写分离与按需拉取:分布式文件系统不提供文件的实时一致性。对于大文件,建议仅在 UI 上展示元数据,在用户点击“预览”或“编辑”时,再触发
fs.copy将文件拉取到本地沙箱进行操作。 - 善用 URI 驱动跨应用流转:在实现“手机图片一键投送到智慧屏”等跨应用流转时,不要直接传递物理路径。应使用
datashare://<device_id>/...格式的分布式 URI,接收端通过 URI 透明访问,结合权限控制保障数据安全。 - 完善的状态机管理:在端云协同或设备间同步时,务必建立完整的状态机(如:空闲、同步中、成功、失败、超时),并通过 UI 给予用户明确的反馈,避免因网络抖动导致的用户困惑。
更多推荐

所有评论(0)