news 2026/4/18 9:11:28

鸿蒙 Electron 实战:跨端文件传输与鸿蒙分布式文件服务集成方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙 Electron 实战:跨端文件传输与鸿蒙分布式文件服务集成方案

在鸿蒙与 Electron 的融合开发中,跨端文件传输是高频核心需求,比如 Electron 桌面端向鸿蒙设备传输配置文件、鸿蒙设备向 Electron 端上传采集的日志文件等。鸿蒙分布式文件服务(Distributed File Service,DFS)提供了跨设备文件访问的能力,将其与 Electron 结合,可实现 “一次传输、多端共享” 的分布式文件管理体系。本文将详细讲解如何集成鸿蒙分布式文件服务到 Electron 应用中,实现 Electron 与鸿蒙设备间的双向文件传输,附带完整代码案例和工程化配置。

一、核心价值与应用场景

1. 核心价值

  • 分布式文件共享:依托鸿蒙 DFS,Electron 与鸿蒙设备可访问同一分布式文件目录,实现文件跨端无缝共享。
  • 高效文件传输:摒弃传统的 “上传 - 下载” 模式,通过鸿蒙分布式能力直接读写跨设备文件,传输效率提升显著。
  • 技术栈复用:前端开发者使用 HTML/JS/TS 即可实现跨端文件操作,无需深入学习鸿蒙底层文件系统 API。

2. 典型应用场景

  • 办公场景:Electron 端编辑的文档,鸿蒙平板端可直接通过分布式文件服务访问并修改。
  • 工业场景:鸿蒙工业设备采集的日志文件,Electron 监控端可实时读取并分析。
  • 智能家居场景:Electron 端下载的设备固件,通过分布式文件服务推送到鸿蒙智能设备完成升级。

二、环境搭建与前置准备

1. 基础环境要求

  • Electron:Node.js(v18+)、Electron(v28+)、electron-builder(打包工具)、form-data(文件上传)、axios(网络请求)
  • 鸿蒙:DevEco Studio(最新版)、鸿蒙 SDK(API 10+)、鸿蒙设备 / 模拟器(开启开发者模式、分布式能力)
  • 鸿蒙分布式文件服务:鸿蒙设备已开启 “多设备协同”,且与 Electron 设备处于同一局域网、同一华为账号下

2. 工程化初始化

2.1 创建 Electron 工程

bash

运行

# 初始化项目 mkdir harmony-electron-file && cd harmony-electron-file npm init -y # 安装核心依赖 npm install electron electron-builder axios form-data fs-extra --save npm install nodemon --save-dev
2.2 配置 package.json

json

{ "name": "harmony-electron-file", "version": "1.0.0", "main": "main/index.js", "scripts": { "start": "electron .", "dev": "nodemon --exec electron .", "build": "electron-builder" }, "build": { "appId": "com.example.harmonyfile", "productName": "HarmonyElectronFile", "directories": { "output": "dist" }, "win": { "target": "nsis" }, "mac": { "target": "dmg" }, "linux": { "target": "deb" } } }
2.3 鸿蒙工程配置(开启分布式文件权限)

在鸿蒙工程的entry/src/main/module.json5中添加分布式文件相关权限:

json5

{ "module": { "name": "entry", "type": "entry", "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }, { "name": "ohos.permission.READ_USER_STORAGE" }, { "name": "ohos.permission.WRITE_USER_STORAGE" }, { "name": "ohos.permission.ACCESS_DISTRIBUTED_FILE_SERVICE" } ], "distributedConfiguration": { "deviceCommunication": true, "dataSync": true, "fileSharing": true } } }

三、核心代码案例:跨端文件传输与分布式文件访问

整体流程说明

  1. 鸿蒙端:搭建 gRPC 服务,实现分布式文件的读取、写入、列表查询接口;暴露文件传输的 HTTP 接口(备用)。
  2. Electron 端:通过 gRPC 调用鸿蒙端的文件接口,实现文件上传、下载、分布式目录查询;利用 Electron 的文件选择器实现本地文件选择。

步骤 1:定义 gRPC 文件传输协议

在项目根目录创建proto/file_service.proto

protobuf

syntax = "proto3"; package file; // 文件元数据 message FileMeta { string file_name = 1; // 文件名 string file_path = 2; // 分布式文件路径 int64 file_size = 3; // 文件大小(字节) int64 update_time = 4; // 更新时间 } // 文件上传请求(流式) message UploadFileRequest { oneof data { FileMeta meta = 1; // 文件元数据(第一个请求) bytes chunk = 2; // 文件分片数据 } } // 文件上传响应 message UploadFileResponse { bool success = 1; string message = 2; FileMeta file_meta = 3; } // 文件下载请求 message DownloadFileRequest { string file_path = 1; // 分布式文件路径 } // 文件下载响应(流式) message DownloadFileResponse { oneof data { FileMeta meta = 1; // 文件元数据(第一个响应) bytes chunk = 2; // 文件分片数据 } } // 文件列表查询请求 message ListFileRequest { string dir_path = 1; // 分布式目录路径 } // 文件列表查询响应 message ListFileResponse { bool success = 1; string message = 2; repeated FileMeta file_list = 3; // 文件列表 } // 文件服务接口 service FileService { // 上传文件(流式) rpc UploadFile (stream UploadFileRequest) returns (UploadFileResponse); // 下载文件(流式) rpc DownloadFile (DownloadFileRequest) returns (stream DownloadFileResponse); // 查询文件列表 rpc ListFiles (ListFileRequest) returns (ListFileResponse); }

步骤 2:鸿蒙端实现 gRPC 文件服务(分布式文件操作)

2.1 鸿蒙端分布式文件工具类

typescript

运行

// entry/src/main/ets/utils/DistributedFileUtil.ets import fileIo from '@ohos.fileio'; import distributedFS from '@ohos.distributedFileSystem'; import path from '@ohos.path'; import fs from '@ohos.file.fs'; // 分布式文件根目录(鸿蒙DFS的共享目录) const DISTRIBUTED_ROOT = 'distributed:///'; // 本地缓存目录 const LOCAL_CACHE_DIR = getContext().cacheDir; // 初始化分布式文件系统 export async function initDistributedFS() { try { // 开启分布式文件服务 await distributedFS.enableDistributedFileSystem(); console.log('分布式文件系统初始化成功'); } catch (error) { console.error('初始化分布式文件系统失败:', error); } } // 获取分布式文件列表 export async function listDistributedFiles(dirPath: string): Promise<Array<{ fileName: string; filePath: string; fileSize: number; updateTime: number; }>> { const fullPath = path.join(DISTRIBUTED_ROOT, dirPath); const fileList: Array<any> = []; try { const dir = fs.opendirSync(fullPath); let entry: fs.Dirent | null = null; do { entry = dir.readSync(); if (entry) { if (!entry.isDirectory()) { const stat = fs.statSync(path.join(fullPath, entry.name)); fileList.push({ fileName: entry.name, filePath: path.join(dirPath, entry.name), fileSize: stat.size, updateTime: stat.mtime.getTime() }); } } } while (entry); dir.closeSync(); return fileList; } catch (error) { console.error('查询分布式文件列表失败:', error); return []; } } // 写入分布式文件(分片) export async function writeDistributedFile(filePath: string, chunks: Buffer[]): Promise<boolean> { const fullPath = path.join(DISTRIBUTED_ROOT, filePath); try { // 创建文件写入流 const fd = fs.openSync(fullPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC); let offset = 0; for (const chunk of chunks) { fs.writeSync(fd, chunk, { offset }); offset += chunk.length; } fs.closeSync(fd); return true; } catch (error) { console.error('写入分布式文件失败:', error); return false; } } // 读取分布式文件(分片) export async function readDistributedFile(filePath: string, chunkSize: number = 1024 * 1024): Promise<Buffer[]> { const fullPath = path.join(DISTRIBUTED_ROOT, filePath); const chunks: Buffer[] = []; try { const stat = fs.statSync(fullPath); const fileSize = stat.size; const fd = fs.openSync(fullPath, fs.OpenMode.READ_ONLY); let offset = 0; while (offset < fileSize) { const buffer = Buffer.alloc(Math.min(chunkSize, fileSize - offset)); const bytesRead = fs.readSync(fd, buffer, { offset }); if (bytesRead > 0) { chunks.push(buffer.slice(0, bytesRead)); } offset += bytesRead; } fs.closeSync(fd); return chunks; } catch (error) { console.error('读取分布式文件失败:', error); return []; } }
2.2 鸿蒙端 gRPC 文件服务实现

typescript

运行

// entry/src/main/ets/utils/FileGrpcServer.ets import grpc from '@ohos/grpc'; import protoLoader from '@ohos/proto-loader'; import { initDistributedFS, listDistributedFiles, writeDistributedFile, readDistributedFile } from './DistributedFileUtil'; import path from '@ohos.path'; // 加载proto文件 const protoPath = path.join(getContext().filesDir, 'file_service.proto'); const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const fileProto = grpc.loadPackageDefinition(packageDefinition).file; // 实现文件服务接口 class FileServiceImpl implements fileProto.FileService { // 上传文件(流式处理) async UploadFile(call: any, callback: any) { let fileMeta: any = null; const chunks: Buffer[] = []; // 监听流式数据 call.on('data', (data: any) => { if (data.meta) { // 第一个数据是文件元数据 fileMeta = data.meta; console.log('接收文件元数据:', fileMeta.file_name); } else if (data.chunk) { // 后续数据是文件分片 chunks.push(Buffer.from(data.chunk)); } }); // 流式数据接收完成 call.on('end', async () => { if (!fileMeta) { callback({ message: '未接收文件元数据' }, null); return; } // 初始化分布式文件系统 await initDistributedFS(); // 写入分布式文件 const success = await writeDistributedFile(fileMeta.file_path, chunks); if (success) { const fileSize = chunks.reduce((sum, chunk) => sum + chunk.length, 0); callback(null, { success: true, message: '文件上传成功', file_meta: { file_name: fileMeta.file_name, file_path: fileMeta.file_path, file_size: fileSize, update_time: Date.now() } }); } else { callback(null, { success: false, message: '文件上传失败', file_meta: fileMeta }); } }); // 监听错误 call.on('error', (err: any) => { console.error('文件上传错误:', err); callback(err, null); }); } // 下载文件(流式响应) async DownloadFile(call: any, callback: any) { const { file_path } = call.request; if (!file_path) { callback({ message: '文件路径不能为空' }, null); return; } try { // 初始化分布式文件系统 await initDistributedFS(); // 读取分布式文件 const chunks = await readDistributedFile(file_path); if (chunks.length === 0) { callback(null, { message: '文件不存在或为空' }); return; } // 获取文件元数据(第一个响应) const fileSize = chunks.reduce((sum, chunk) => sum + chunk.length, 0); call.write({ meta: { file_name: path.basename(file_path), file_path: file_path, file_size: fileSize, update_time: Date.now() } }); // 发送文件分片 for (const chunk of chunks) { call.write({ chunk: chunk }); } // 完成流式响应 call.end(); callback(null, null); } catch (error) { console.error('文件下载错误:', error); callback(error, null); } } // 查询文件列表 async ListFiles(call: any, callback: any) { const { dir_path } = call.request; // 初始化分布式文件系统 await initDistributedFS(); // 查询文件列表 const fileList = await listDistributedFiles(dir_path || '/'); callback(null, { success: true, message: `查询到${fileList.length}个文件`, file_list: fileList.map(file => ({ file_name: file.fileName, file_path: file.filePath, file_size: file.fileSize, update_time: file.updateTime })) }); } } // 启动gRPC文件服务 export function startFileGrpcServer() { const server = new grpc.Server(); server.addService(fileProto.FileService.service, new FileServiceImpl()); // 绑定端口(50052,与之前的服务区分) server.bindAsync('0.0.0.0:50052', grpc.ServerCredentials.createInsecure(), (err: any, port: number) => { if (err) { console.error('gRPC文件服务启动失败:', err); return; } server.start(); console.log(`鸿蒙gRPC文件服务已启动,端口: ${port}`); }); }
2.3 鸿蒙端页面初始化 gRPC 服务

typescript

运行

// entry/src/main/ets/pages/Index.ets import { startFileGrpcServer } from '../utils/FileGrpcServer'; @Entry @Component struct Index { @State message: string = '鸿蒙分布式文件服务初始化中...'; aboutToAppear() { // 启动gRPC文件服务 startFileGrpcServer().then(() => { this.message = '鸿蒙gRPC文件服务已启动,等待Electron调用...'; }).catch((err) => { this.message = `服务启动失败:${err.message}`; }); } build() { Column() { Text(this.message) .fontSize(20) .fontWeight(FontWeight.Bold) .margin({ top: 100 }) .textAlign(TextAlign.Center); } .width('100%') .height('100%') .backgroundColor(Color.White); } }

步骤 3:Electron 端实现 gRPC 客户端与文件操作

3.1 Electron 主进程 gRPC 客户端工具类

javascript

运行

// main/grpc/fileClient.js const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const path = require('path'); const fs = require('fs-extra'); // 加载proto文件 const protoPath = path.join(__dirname, '../../proto/file_service.proto'); const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const fileProto = grpc.loadPackageDefinition(packageDefinition).file; // 创建gRPC客户端 const fileClient = new fileProto.FileService( '192.168.1.100:50052', // 替换为鸿蒙设备IP grpc.credentials.createInsecure() ); // 上传文件到鸿蒙分布式文件系统 async function uploadFileToHarmony(localFilePath, remoteFilePath) { return new Promise((resolve, reject) => { // 创建流式调用 const call = fileClient.UploadFile((err, response) => { if (err) reject(err); else resolve(response); }); // 先发送文件元数据 const fileName = path.basename(localFilePath); call.write({ meta: { file_name: fileName, file_path: remoteFilePath, file_size: fs.statSync(localFilePath).size, update_time: Date.now() } }); // 读取本地文件并分片发送 const stream = fs.createReadStream(localFilePath, { highWaterMark: 1024 * 1024 }); stream.on('data', (chunk) => { call.write({ chunk: chunk }); }); stream.on('end', () => { call.end(); }); stream.on('error', (err) => { call.destroy(err); reject(err); }); }); } // 从鸿蒙分布式文件系统下载文件 async function downloadFileFromHarmony(remoteFilePath, localFilePath) { return new Promise((resolve, reject) => { // 创建写入流 const stream = fs.createWriteStream(localFilePath); let fileMeta = null; // 调用下载接口(流式响应) const call = fileClient.DownloadFile({ file_path: remoteFilePath }); call.on('data', (data) => { if (data.meta) { // 接收文件元数据 fileMeta = data.meta; console.log('接收文件元数据:', fileMeta.file_name); } else if (data.chunk) { // 接收文件分片并写入 stream.write(data.chunk); } }); call.on('end', () => { stream.end(); resolve({ success: true, file_meta: fileMeta, local_path: localFilePath }); }); call.on('error', (err) => { stream.destroy(err); reject(err); }); }); } // 查询鸿蒙分布式文件列表 async function listHarmonyFiles(dirPath = '/') { return new Promise((resolve, reject) => { fileClient.ListFiles({ dir_path: dirPath }, (err, response) => { if (err) reject(err); else resolve(response); }); }); } module.exports = { uploadFileToHarmony, downloadFileFromHarmony, listHarmonyFiles };
3.2 Electron 主进程整合文件操作与 IPC 通信

javascript

运行

// main/index.js const { app, BrowserWindow, ipcMain, dialog } = require('electron'); const path = require('path'); const { uploadFileToHarmony, downloadFileFromHarmony, listHarmonyFiles } = require('./grpc/fileClient'); let mainWindow; function createWindow() { mainWindow = new BrowserWindow({ width: 1000, height: 700, webPreferences: { preload: path.join(__dirname, '../preload/index.js'), contextIsolation: true, sandbox: false } }); mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')); mainWindow.webContents.openDevTools(); } // 监听渲染进程的文件操作请求 ipcMain.handle('select-local-file', async () => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], filters: [{ name: 'All Files', extensions: ['*'] }] }); return result.filePaths[0] || ''; }); ipcMain.handle('upload-file', async (event, localFilePath, remoteFilePath) => { return await uploadFileToHarmony(localFilePath, remoteFilePath); }); ipcMain.handle('download-file', async (event, remoteFilePath, localFilePath) => { return await downloadFileFromHarmony(remoteFilePath, localFilePath); }); ipcMain.handle('list-files', async (event, dirPath) => { return await listHarmonyFiles(dirPath); }); app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
3.3 Electron 预加载脚本暴露 API

javascript

运行

// preload/index.js const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronApi', { // 选择本地文件 selectLocalFile: () => ipcRenderer.invoke('select-local-file'), // 上传文件到鸿蒙 uploadFile: (localFilePath, remoteFilePath) => ipcRenderer.invoke('upload-file', localFilePath, remoteFilePath), // 从鸿蒙下载文件 downloadFile: (remoteFilePath, localFilePath) => ipcRenderer.invoke('download-file', remoteFilePath, localFilePath), // 查询鸿蒙文件列表 listFiles: (dirPath) => ipcRenderer.invoke('list-files', dirPath) });
3.4 Electron 渲染进程 UI 实现

html

预览

<!-- renderer/index.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>鸿蒙Electron分布式文件传输</title> <style> body { font-family: Arial, sans-serif; padding: 20px; background-color: #f5f5f5; } .container { max-width: 900px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .file-operation { margin-bottom: 20px; padding: 10px; border-bottom: 1px solid #eee; } button { padding: 8px 16px; margin: 0 5px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #0056b3; } input { padding: 8px; margin: 0 5px; width: 300px; border: 1px solid #ccc; border-radius: 4px; } #fileList { margin-top: 20px; border: 1px solid #eee; border-radius: 4px; padding: 10px; max-height: 400px; overflow-y: auto; } .file-item { padding: 8px; border-bottom: 1px solid #f5f5f5; display: flex; justify-content: space-between; align-items: center; } .file-item:last-child { border-bottom: none; } .status { margin-top: 10px; padding: 10px; border-radius: 4px; background-color: #e9ecef; } </style> </head> <body> <div class="container"> <h1>鸿蒙Electron分布式文件传输</h1> <div class="file-operation"> <h3>文件上传</h3> <input type="text" id="localFile" placeholder="请选择本地文件" readonly> <button onclick="selectLocalFile()">选择文件</button> <input type="text" id="remotePath" placeholder="鸿蒙分布式文件路径(如/test.txt)" value="/test.txt"> <button onclick="uploadFile()">上传文件</button> </div> <div class="file-operation"> <h3>文件下载</h3> <input type="text" id="downloadRemotePath" placeholder="鸿蒙分布式文件路径" value="/test.txt"> <button onclick="downloadFile()">下载文件</button> </div> <div class="file-operation"> <h3>文件列表</h3> <button onclick="refreshFileList()">刷新列表</button> <div id="fileList">等待加载...</div> </div> <div class="status" id="status">就绪</div> </div> <script> const localFileInput = document.getElementById('localFile'); const remotePathInput = document.getElementById('remotePath'); const downloadRemotePathInput = document.getElementById('downloadRemotePath'); const fileListDiv = document.getElementById('fileList'); const statusDiv = document.getElementById('status'); // 日志更新函数 function updateStatus(message, isError = false) { const now = new Date().toLocaleString(); statusDiv.innerHTML = `[${now}] ${message}`; statusDiv.style.color = isError ? 'red' : 'black'; } // 选择本地文件 async function selectLocalFile() { try { const filePath = await window.electronApi.selectLocalFile(); if (filePath) { localFileInput.value = filePath; updateStatus(`已选择文件:${filePath}`); } } catch (error) { updateStatus(`选择文件失败:${error.message}`, true); } } // 上传文件 async function uploadFile() { const localFilePath = localFileInput.value; const remoteFilePath = remotePathInput.value; if (!localFilePath) { updateStatus('请先选择本地文件', true); return; } try { updateStatus('正在上传文件...'); const response = await window.electronApi.uploadFile(localFilePath, remoteFilePath); updateStatus(`文件上传成功:${JSON.stringify(response)}`); refreshFileList(); } catch (error) { updateStatus(`文件上传失败:${error.message}`, true); } } // 下载文件 async function downloadFile() { const remoteFilePath = downloadRemotePathInput.value; if (!remoteFilePath) { updateStatus('请输入鸿蒙分布式文件路径', true); return; } try { updateStatus('正在下载文件...'); // 保存到桌面 const desktopPath = require('path').join(window.electronApi.getDesktopPath(), path.basename(remoteFilePath)); const response = await window.electronApi.downloadFile(remoteFilePath, desktopPath); updateStatus(`文件下载成功:${desktopPath}`, false); } catch (error) { updateStatus(`文件下载失败:${error.message}`, true); } } // 刷新文件列表 async function refreshFileList() { try { updateStatus('正在加载文件列表...'); const response = await window.electronApi.listFiles('/'); fileListDiv.innerHTML = ''; if (response.file_list.length === 0) { fileListDiv.innerHTML = '暂无文件'; } else { response.file_list.forEach(file => { const div = document.createElement('div'); div.className = 'file-item'; div.innerHTML = ` <div> <span>${file.file_name}</span> <span>(大小:${(file.file_size / 1024).toFixed(2)}KB)</span> <span>(更新时间:${new Date(file.update_time).toLocaleString()})</span> </div> <button onclick="setDownloadPath('${file.file_path}')">下载</button> `; fileListDiv.appendChild(div); }); } updateStatus(`加载完成,共${response.file_list.length}个文件`); } catch (error) { updateStatus(`加载文件列表失败:${error.message}`, true); } } // 设置下载路径 function setDownloadPath(filePath) { downloadRemotePathInput.value = filePath; } // 初始化加载文件列表 window.onload = refreshFileList; // 补充:暴露桌面路径获取(需在预加载脚本添加) contextBridge.exposeInMainWorld('electronApi', { ...window.electronApi, getDesktopPath: () => ipcRenderer.invoke('get-desktop-path') }); ipcMain.handle('get-desktop-path', () => app.getPath('desktop')); </script> </body> </html>

四、运行与测试流程

1. 鸿蒙侧运行

  1. 在 DevEco Studio 中运行鸿蒙工程到真机 / 模拟器,确保 gRPC 文件服务启动成功。
  2. 确认鸿蒙设备已开启分布式能力,且与 Electron 设备处于同一局域网。

2. Electron 侧运行

  1. 修改 Electron 主进程中的鸿蒙设备 IP 地址。
  2. 执行npm run start启动 Electron 应用。
  3. 测试步骤
    • 点击 “选择文件”,选择本地任意文件。
    • 输入鸿蒙分布式文件路径,点击 “上传文件”,验证文件是否上传成功。
    • 点击 “刷新列表”,查看鸿蒙分布式文件列表。
    • 选择文件点击 “下载”,验证文件是否下载到本地桌面。

五、工程化优化与避坑指南

1. 优化建议

  • 文件分片大小调整:根据网络状况调整分片大小(默认 1MB),网络差时减小分片,提升传输稳定性。
  • 传输进度显示:在 Electron 端添加文件传输进度条,提升用户体验。
  • 文件校验:传输完成后通过 MD5 校验文件完整性,避免文件损坏。
  • 大文件支持:采用流式处理,避免一次性加载大文件导致内存溢出。

2. 常见坑点与解决方案

  • gRPC 流式传输失败:确保鸿蒙与 Electron 的 gRPC 版本兼容,避免使用过高版本的 gRPC 依赖。
  • 分布式文件权限不足:鸿蒙设备需开启 “多设备协同”,并授予应用存储和分布式文件权限。
  • 文件路径错误:鸿蒙分布式文件路径以distributed:///开头,避免使用本地绝对路径。
  • 跨设备访问失败:确保所有设备登录同一华为账号,且处于同一局域网、同一分布式网络中。

六、总结

本文通过 gRPC 流式传输结合鸿蒙分布式文件服务,实现了 Electron 与鸿蒙设备间的双向文件传输和分布式文件管理。这种方案充分利用了鸿蒙 DFS 的分布式能力和 gRPC 的高效流式传输特性,解决了传统跨端文件传输的效率低、兼容性差的问题。

开发者可基于本文的思路,拓展更多功能,如文件重命名、删除、批量传输,或结合鸿蒙推送服务实现文件传输完成后的消息通知,进一步完善分布式文件管理体系。随着鸿蒙生态的持续发展,Electron 与鸿蒙的融合将为跨端文件处理场景带来更多创新可能。

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:00:55

Foundation 图片

Foundation 图片&#xff08;Thumbnail & 响应式图片&#xff09;详解&#xff08;超级完整版&#xff0c;一次讲透&#xff09; 我们继续你的 Foundation 系列&#xff0c;今天重点讲 图片处理&#xff1a;Thumbnail&#xff08;缩略图样式&#xff09;和响应式图片&#…

作者头像 李华
网站建设 2026/4/18 7:39:35

Foundation 列表

Foundation 列表&#xff08;Menu&#xff09;详解&#xff08;超级完整版&#xff0c;一次讲透&#xff09; 我们继续你的 Foundation 系列&#xff0c;今天把 列表&#xff08;主要是 Menu 组件&#xff09;讲得清清楚楚&#xff01;Foundation 6 中的 Menu 是最强大的列表系…

作者头像 李华
网站建设 2026/4/18 7:59:58

谷歌紧急修复已遭利用的 Chrome 新 0day,无CVE编号

聚焦源代码安全&#xff0c;网罗国内外最新资讯&#xff01; 编译&#xff1a;代码卫士 谷歌紧急修复了位于 Chrome 中的一个已遭利用 0day 漏洞。该漏洞尚未分配 CVE 编号&#xff0c;是谷歌今年修复的第八个 0day 漏洞。 谷歌在当地时间本周三发布的一份安全公告中提到&#…

作者头像 李华
网站建设 2026/4/18 7:03:11

字节跳动提示工程架构师:我常用的5个工具,推荐给你!

字节跳动提示工程架构师的5大核心工具&#xff1a;从落地到优化的实战选择 元数据框架 标题&#xff1a;字节跳动提示工程架构师的5大核心工具&#xff1a;从落地到优化的实战选择关键词&#xff1a;提示工程, 大模型工具链, 字节实战, Prompt结构化, 上下文管理, 效果评估, 多…

作者头像 李华
网站建设 2026/4/17 18:11:32

AI如何快速解决Java数据库连接异常问题

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个Java程序&#xff0c;演示如何处理java.sql.SQLNonTransientConnectionException异常。程序应包含以下功能&#xff1a;1) 尝试连接MySQL数据库&#xff1b;2) 捕获并处理连…

作者头像 李华
网站建设 2026/4/18 7:33:16

零基础图解:Ubuntu安装Python就像用手机APP

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 生成面向Linux新手的Python安装指南&#xff0c;要求&#xff1a;1. 所有命令附带截图示例 2. 解释sudo、apt等基础概念 3. 包含如何复制终端命令等细节 4. 用绿色/红色标注成功和错…

作者头像 李华