嗨~我是小L!在鸿蒙开发中,文件分享就像「数字高速公路」——URI和FD两种方式各有优势。今天带你掌握安全高效的分享技巧,让应用间数据交换更流畅~

一、两种分享方式:URI vs FD🚗

核心差异对比表

| 维度 | URI分享 | FD分享 |
|----------------|---------------------------|---------------------------|
| 操作难度 | 简单(系统自动管理权限) | 复杂(需手动管理文件句柄)|
| 分享单位 | 单个文件 | 单个文件或目录 |
| 权限时效 | 临时授权(接收方退出即失效)| 永久授权(FD关闭后失效) |
| 典型场景 | 文本、图片临时分享 | 大文件、目录批量传输 |

适用场景选择

  • 选URI:聊天App发图片、邮件附件分享
  • 选FD:文件管理器传输文件夹、音视频应用批量导入

二、URI分享:「即点即走」的轻量级方案✨

1. 分享端实现(以图片为例)

import { fileUri, wantConstant } from '@ohos.fileUri';  
import { UIAbility, Want } from '@ohos.app.ability';  

export default class ShareAbility extends UIAbility {  
  onWindowStageCreate() {  
    // 1. 获取文件URI  
    const imagePath = `${this.context.filesDir}/poster.jpg`;  
    const uri = fileUri.getUriFromPath(imagePath);  
    
    // 2. 创建分享意图(授予读写权限)  
    const want: Want = {  
      action: wantConstant.Action.SEND_DATA,  
      uri: uri,  
      type: 'image/jpeg',  
      flags: wantConstant.Flag.AUTH_READ_URI_PERMISSION |  
             wantConstant.Flag.AUTH_WRITE_URI_PERMISSION  
    };  
    
    // 3. 发起分享  
    this.context.startAbility(want).then(() => {  
      console.log('图片分享成功');  
    });  
  }  
}  

2. 接收端处理(保存文件到沙箱)

export default class ReceiveAbility extends UIAbility {  
  onNewWant(want: Want) {  
    if (want.uri) {  
      const sourceUri = want.uri;  
      const destPath = `${this.context.filesDir}/received_${Date.now()}.jpg`;  
      
      // 读取URI文件并写入本地  
      fileIo.copySync(sourceUri, destPath);  
      console.log('文件已保存至:', destPath);  
    }  
  }  
}  

3. 权限配置(module.json5)

{  
  "abilities": [  
    {  
      "name": ".ShareAbility",  
      "skills": [  
        {  
          "actions": ["ohos.arkui.intent.action.SEND_DATA"],  
          "uris": [  
            {  
              "scheme": "file",  
              "host": "*",  
              "path": "/data/storage/el1/*" // 允许分享的路径范围  
            }  
          ]  
        }  
      ]  
    }  
  ]  
}  

三、FD分享:「大文件专用」的高效通道🚚

1. 分享目录示例(如文档文件夹)

import { fileIO as fs } from '@ohos.fileIO';  
import { FileDescriptor } from '@ohos.fs';  

function shareDirectory(sourceDir: string) {  
  // 1. 打开目录获取FD  
  const fd: FileDescriptor = fs.openSync(sourceDir, fs.OpenMode.READ);  
  
  // 2. 创建FD分享数据  
  const shareData = {  
    fd: fd,  
    type: 'directory',  
    metadata: {  
      name: 'ProjectFiles',  
      size: fs.statSync(sourceDir).size  
    }  
  };  
  
  // 3. 通过自定义通道传递FD(如IPC)  
  ipc.send('SHARE_FD', shareData);  
}  

2. 接收端处理FD(读取目录内容)

function handleReceivedFD(fd: FileDescriptor) {  
  // 1. 读取目录下所有文件  
  const entries = fs.readdirSync(fd);  
  
  // 2. 遍历处理文件  
  entries.forEach((entry) => {  
    if (entry.isFile()) {  
      const fileFd = fs.openSync(`${fd.path}/${entry.name}`, fs.OpenMode.READ);  
      const content = fs.readFileSync(fileFd, 'utf8');  
      fs.closeSync(fileFd);  
    }  
  });  
  
  // 3. 关闭FD释放资源  
  fs.closeSync(fd);  
}  

3. 安全注意事项

  • FD生命周期:接收方必须在FD关闭前完成操作,否则会导致文件不可访问
  • 权限控制:仅允许分享已授权的目录(如应用沙箱内的files/目录)

四、安全控制:数据交换的「交通规则」🛑

1. 权限最小化原则

  • 仅授予必要权限:

    // 仅授予读权限(避免接收方篡改文件)  
    flags: wantConstant.Flag.AUTH_READ_URI_PERMISSION  

2. 敏感数据加密

// 分享前加密文件  
import { crypto } from '@ohos.security';  

async function encryptAndShare(filePath: string) {  
  const key = crypto.generateKey('AES', 256);  
  const encryptedData = await crypto.encryptFile(filePath, key);  
  const uri = fileUri.getUriFromPath(encryptedData.path);  
  // 分享加密后的URI  
  shareUri(uri);  
}  

3. 临时权限管理

  • URI分享自动回收权限:接收方应用退出后,URI自动失效
  • FD分享需手动关闭:

    // 接收方使用完FD后立即关闭  
    fs.closeSync(fd);  

五、实战场景:跨设备批量文件传输📱→💻

场景描述

用户通过鸿蒙「一碰传」将手机中的文件夹快速分享到平板:

  1. 手机端(分享方)

    • 选择目标文件夹,生成FD
    • 通过分布式软总线传输FD和元数据
  2. 平板端(接收方)

    • 接收FD,验证目录权限
    • 遍历FD读取文件,保存至本地

核心代码(简化版)

// 手机端:通过软总线发送FD  
import { distributedBus } from '@ohos.distributedBus';  

const bus = distributedBus.create('file_share_channel');  
bus.on('receive_fd', (fdData) => {  
  // 平板端接收FD后处理  
  handleReceivedFD(fdData.fd);  
});  

// 平板端:接收并处理FD  
function handleReceivedFD(fd: FileDescriptor) {  
  // 检查是否为允许的分享目录  
  if (!isAuthorized(fd.path)) {  
    fs.closeSync(fd);  
    throw new Error('非法目录');  
  }  
  // 执行文件复制逻辑  
}  

六、避坑指南⚠️

1. URI分享常见问题

  • 权限不足:未在module.json5声明SEND_DATA动作,导致分享失败
  • 文件路径错误:使用绝对路径而非沙箱路径,例如/sdcard/可能无权访问

2. FD分享注意事项

  • FD泄漏:忘记关闭FD会导致文件句柄耗尽,需在finally块中关闭

    try {  
      const fd = fs.openSync(...);  
      // 使用FD  
    } finally {  
      fs.closeSync(fd);  
    }  
  • 跨进程问题:FD仅在当前进程有效,跨进程传递需通过dup()复制

3. 测试要点

  • 模拟低权限场景:验证未授权文件是否无法访问
  • 压力测试:同时分享多个大文件,观察系统资源占用

总结:分享方案「三选法则」

  1. 轻量临时:选URI(如社交分享、单文件传输)
  2. 大量持久:选FD(如文件管理、目录迁移)
  3. 敏感数据:必加密+临时权限(防止数据泄露)

lyc233333
1 声望0 粉丝