HarmonyOS社交通讯应用开发 28:分布式文件系统中媒体数据读取显示
引言接续的最后一公里是媒体还原设备 B 收到了attachmentsAsset 描述数组也通过分布式文件系统在本地distributedFilesDir拿到了文件实体接下来要做的是——把文件读出来图片重建回PixelMap让九宫格能显示视频拿到可播放的 uri并把这些文件从分布式目录拷贝到应用私有目录留作本地使用。这段逻辑集中在entry/src/main/ets/utils/FileUtil.ets的fileCopy函数里由第 25 篇讲过的restored回调逐附件调用。还原流程的关键点有三个能力检查canIUse、从 name 解析类型与文件名、图片/视频分叉重建。逐个展开。先交代还原发生的时机与次序这决定了fileCopy内部为什么这样写。回顾第 25 篇接收端在status restored时才取回attachments并逐附件调用fileCopy。也就是说媒体还原严格排在描述数据可用之后。但注意描述可用 ≠ 文件可用——attachments是随分布式数据对象同步的快文件实体是随分布式文件系统同步的慢两者存在时间差。因此fileCopy内部第一步就是accessSync探测文件是否已在本地分布式目录就绪没就绪就跳过本次还原不报错、不阻塞其他附件。这是接续还原中最容易被忽略、却最体现工程经验的一个细节。一、能力检查canIUse 守卫fileCopy的第一行是能力判断exportfunctionfileCopy(context: common.UIAbilityContext, attachment: commonType.Asset, mediaUriArray:ArrayMediaInfo):void{if(canIUse(SystemCapability.DistributedDataManager.CommonType)) {// ... 完整还原逻辑} }canIUse(SystemCapability.DistributedDataManager.CommonType)用于检测当前设备是否支持分布式数据管理的CommonType能力即commonType.Asset等类型是否可用。这是一个运行时能力探测不同设备/系统版本的 API 集合可能不同直接在能力判断后使用相关类型可以避免在低版本设备上因 API 不存在而崩溃。虽然本项目 API 23 必然支持但保留这个判断是面向多设备、多版本的稳健写法——与第 24 篇提到的设备兼容性一脉相承。二、从 name 解析类型与文件名还原的第一步是解析 Asset 的name。回顾第 27 篇发送端getAssetInfo构造的name形如image_uuid/video_uuid——前半段是类型后半段是文件名。接收端用字符串 API 拆开letmediaName attachment.name.substring(attachment.name.indexOf(_) 1);letmediaType attachment.name.substring(0, attachment.name.indexOf(_));indexOf(_)找到第一个下划线的位置substring(0, indexOf(_))取类型image或video正好对应MediaType枚举的字符串值substring(indexOf(_) 1)取文件名去扩展名如 UUID 或图片名。这两行是整个媒体还原的解包动作发送端拼好的name接收端原样拆回两个信息然后拼出分布式文件路径与本地保存路径letfilePath:string context.distributedFilesDir / mediaName;// 分布式目录中的源文件letsavePath:string context.filesDir / mediaName;// 应用私有目录中的目标文件filePath是设备 B 自己的distributedFilesDir下、与发送端同名的文件分布式文件系统已同步过来见第 26 篇savePath是应用私有目录——还原的同时把文件落地到本地沙箱之后即使分布式文件失效本地副本依然可用。三、读取源文件重建图片 PixelMap主体逻辑是打开源文件 → 读入 Buffer → 按类型重建 → 写回本地letfile: fileIo.File| undefined undefined;letsaveFile: fileIo.File| undefined undefined;letimageSourceApi: image.ImageSource| undefined;try{if(fileIo.accessSync(filePath)) {//源文件存在才继续//1. 打开本地目标文件可写可建与分布式源文件只读 saveFile fileIo.openSync(savePath,fileIo.OpenMode.READ_WRITE |fileIo.OpenMode.CREATE); file fileIo.openSync(filePath,fileIo.OpenMode.READ_WRITE);//2. 按Asset.size 分配Buffer读取第一段数据letbuf:ArrayBuffernewArrayBuffer(Number(attachment.size));letreadSize 0;letreadLen fileIo.readSync(file.fd,buf, {offset:readSize});if(mediaTypeMediaType.MEDIA_IMAGE) {//3a. 图片从Buffer创建ImageSource并同步生成PixelMapletsourceOptions: image.SourceOptions { sourceDensity: 120 }; imageSourceApi image.createImageSource(buf,sourceOptions); mediaUriArray.push({ imagePixelMap: imageSourceApi.createPixelMapSync(),//重建PixelMapmediaName: mediaName, mediaType: mediaType}); }elseif(mediaTypeMediaType.MEDIA_VIDEO) {//3b. 视频保留 uri直接放入媒体列表 mediaUriArray.push({ videoUri: attachment.uri,//分布式文件 uri可直接播放 mediaName: mediaName, mediaType: mediaType}); }//4. 循环读完剩余数据写入本地文件while(readLen 0) { readSize readLen; fileIo.writeSync(saveFile.fd,buf); readLen fileIo.readSync(file.fd,buf, {offset:readSize}); } hilog.info(DOMAIN,TAG,FORMAT,${attachment.name} synchronized successfully.); } } catch (error) {...} finally {//5. 关闭文件、释放ImageSourceif(file) { fileIo.closeSync(file.fd); }if(saveFile) { fileIo.closeSync(saveFile.fd); }if(imageSourceApi) { imageSourceApi.release(); imageSourceApi undefined; } }图片分支是重点image.createImageSource(buf, sourceOptions)从读到的 Buffer 创建ImageSourcesourceDensity: 120指定源密度随后createPixelMapSync()同步生成PixelMap。createPixelMapSync是同步版本——restored回调里逐附件还原用同步 API 保证代码顺序清晰、每个附件处理完立即 push 进mediaUriArray。最终 push 进媒体列表的MediaInfo与发送端的结构完全一致imagePixelMapmediaNamemediaTypeUI 的AddMedia九宫格直接复用同一套渲染逻辑if (item.imagePixelMap)显示Image收发两端共用一个 UI 模型——这就是MediaInfo设计的意义。把图片重建这步拆细一点有三个值得理解的点createImageSource(buf, sourceOptions)的两个参数第一个是图片数据的 BufferJPEG 编码第二个是SourceOptions这里只设置了sourceDensity: 120。sourceDensity表示图片的源像素密度120 是缩略图场景的常见取值与屏幕真实密度无关——它主要影响解码时像素尺寸的换算基准。对九宫格缩略图而言这个值足够。**createPixelMapSync()与createPixelMap()**前者同步返回PixelMap后者异步返回PromisePixelMap。还原是逐附件串行 立即入数组的流程同步版让代码平铺直叙若图片多且大可改用异步版配合Promise.all并发解码本例为了清晰选择了同步。imageSourceApi.release()的对称性ImageSource创建后持有解码资源用完必须release()。注意项目把imageSourceApi声明在 try 之外、finally 里释放保证无论解码成功还是中途异常资源都被回收——这是资源获取即初始化、块结束即释放的标准姿势。四、视频分支uri 直达视频分支非常简单mediaUriArray.push({videoUri:attachment.uri,// Asset 里携带的分布式文件 urimediaName:mediaName,mediaType:mediaType});视频不需要重建任何对象——attachment.uri是发送端getAssetInfo里用fileUri.getUriFromPath(filePath)生成的分布式文件 uri设备 B 拿到后可直接交给Video组件播放AddMedia.ets中Video({ src: item.videoUri, ... })。为什么图片要重建 PixelMap、视频却能直接用 uri因为 UI 上图片组件需要PixelMap或图片资源而Image组件虽支持 uri但项目选择统一用 PixelMap 渲染视频组件则天然支持 uri 播放无需转换。这一分叉体现了按消费端需求重建的原则还原到什么形态取决于界面组件要什么。五、读改写把分布式文件落地到本地无论图片还是视频文件本身都要从distributedFilesDir**拷贝到filesDir**。实现用的是读改写循环letbuf:ArrayBuffernewArrayBuffer(Number(attachment.size));letreadSize 0;letreadLen fileIo.readSync(file.fd, buf, {offset: readSize });// ... 类型处理 ...while(readLen 0) { readSize readLen; fileIo.writeSync(saveFile.fd, buf);// 把已读数据写入本地readLen fileIo.readSync(file.fd, buf, {offset: readSize });// 继续读下一段}为什么不用第 26 篇的copyFileSync一是演示两种拷贝手法二是这段代码在读取过程中已经需要 Buffer 供图片分支重建 PixelMap——既然 Buffer 已经在手顺手writeSync落盘即可避免二次 IO。readSync的offset语义是从文件的第 offset 字节开始读每次循环推进readSize直到readLen为 0文件读完。注意new ArrayBuffer(Number(attachment.size))按 Asset 记录的字节数分配缓冲正好容纳整个文件。把循环的执行过程走一遍会更清楚假设附件 1000 字节、单次readSync读满 1000 字节——第一次调用后readLen 1000进入循环readSize变 1000writeSync把整个 Buffer 写入本地文件再readSync发现已到文件尾返回 0循环退出。若文件大于 Bufferattachment.size比实际文件小或一次读不满循环会多次读一段、写一段直到读完。Buffer 复用的细节writeSync写入的是buf整体而readSync每次都覆盖buf的内容所以循环里始终是最新一段数据写盘不会写重复。唯一的假设是attachment.size与实际文件大小一致——这正是发送端statSync取size的用途。finally块收尾三件事关闭源文件与目标文件的fd、imageSourceApi.release()释放图片解码资源。release很关键——ImageSource持有底层解码内存不释放会造成内存泄漏尤其在媒体较多的场景。六、失败处理与容错fileCopy的容错设计值得单独说因为它决定了接续失败时应用会不会崩try {if(fileIo.accessSync(filePath)) { ... }// 文件未就绪 → 静默跳过} catch (error) { leterr: BusinessError errorasBusinessError; hilog.error(DOMAIN, TAG,FORMAT, ${attachment.name} fileCopy failed witherr:${JSON.stringify(err)}); } finally { ... }accessSync守卫文件还没同步到本地时直接跳过整个还原逻辑不抛异常。accessSync对不存在的路径返回 false或抛错正好充当文件是否就绪的探针。catch 只记日志任何一步出错打开失败、解码失败、写盘失败都记录带attachment.name的错误日志便于按附件定位问题但不会中断restored回调里对其他附件的还原——一个附件坏了其余照常。finally 兜底资源无论走哪条路径文件描述符与ImageSource都保证被释放避免失败路径上的资源泄漏。这种单附件隔离 静默降级的容错思路让整个媒体还原在部分失败时依然可用某个视频没同步过来九宫格里只是少一项而不是白屏或崩溃。七、完整链路回看把fileCopy放回第 25 篇的restored回调中看整体if(status restored) {// ... 六字段写入 AppStorage ...let attachments this.distributedObject[attachments]ascommonType.Assets;for(constattachment of attachments) { fileCopy(this.context, attachment,this.mediaUriArray);// 逐附件还原} AppStorage.setOrCreateArrayMediaInfo(mediaUriArray,this.mediaUriArray);// 一次性发布}每个附件走一遍fileCopy结果累积进this.mediaUriArray全部还原后一次性setOrCreate(mediaUriArray, ...)写进AppStorage——AddMedia的StorageLink(mediaUriArray)收到新数组九宫格一次性渲染出所有图片与视频。逐附件还原、整体发布的设计避免每还原一个就触发一次 UI 刷新。八、验证与常见问题如何确认媒体还原成功两个观察点一是hilog中每个附件都会打一行xxx synchronized successfully.附件数与attachments数组长度一致即说明全部还原二是九宫格 UI——图片显示缩略图、视频可播放且应用私有目录filesDir下出现同名文件可在 DevEco Studio 的设备文件管理器里查看。几个常见问题与排查方向图片不显示但视频正常优先怀疑图片解码——检查fileCopy日志是否有该附件的fileCopy failed记录以及发送端packToData时format: image/jpeg与接收端createImageSource是否匹配。附件数量对不上attachments是随分布式数据对象到达的mediaUriArray是逐附件还原的若restored回调里for循环执行次数少于attachments.length多半是某个附件accessSync探测失败被跳过——等文件同步完成后再次接续即可或加大重试。视频首次播放黑屏Video组件用attachment.uri播放若分布式文件尚未完全同步uri 指向的文件可能不完整。写回filesDir的本地副本正是规避手段之一——生产实现可以在本地副本就绪后用本地路径播放。fileCopy被整体跳过检查canIUse(SystemCapability.DistributedDataManager.CommonType)是否返回 false——模拟器或旧版本可能不支持该能力此时媒体还原静默降级为仅还原文本属预期行为。理解这些现象就真正吃透了描述与文件双通道、各自异步到达的还原模型。小结接续后媒体还原是发送端打包的镜像过程canIUse守卫能力可用性从attachment.name拆出类型image/video与文件名从distributedFilesDir读文件图片用createImageSource createPixelMapSync重建PixelMap进媒体列表视频则保留attachment.uri直达播放文件同时以读改写方式落地到filesDir留作本地副本最后整体发布到AppStorage驱动九宫格刷新。至此从设备 A 编辑、打包、同步到设备 B 还原、重建、渲染的完整接续闭环全部打通——六篇文章分别覆盖了原理、对象、还原、文件、模型、媒体六个侧面合起来正是 ContinuePublish 这个示例工程的全部接续技术骨架。

相关新闻

BetterJoy配置指南:5分钟把Switch手柄变成PC上的XInput设备

BetterJoy配置指南:5分钟把Switch手柄变成PC上的XInput设备

BetterJoy配置指南:5分钟把Switch手柄变成PC上的XInput设备 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitcode…

2026/8/26 9:06:45 阅读更多 →
智能招聘系统:AI如何重塑人才匹配与筛选

智能招聘系统:AI如何重塑人才匹配与筛选

1. 智能招聘系统的行业变革背景2026年的招聘市场正在经历一场前所未有的技术革命。传统招聘平台依靠人工筛选简历、电话邀约面试的模式,正在被新一代智能招聘系统彻底颠覆。这些系统通过深度学习算法和大数据分析,正在重构人才匹配的底层逻辑。过去三年间…

2026/8/26 9:06:58 阅读更多 →
HTML 的 <input> 元素

HTML 的 <input> 元素

引言 <input> 元素是 HTML 表单中最核心、最常用的元素之一&#xff0c;它允许用户通过多种方式输入数据。从简单的文本输入框到复杂的文件上传控件&#xff0c;<input> 元素通过其 type 属性的不同取值&#xff0c;展现出强大的灵活性和功能性。本文将全面解析 &…

2026/8/25 6:06:44 阅读更多 →

最新新闻

从网页到PDF:高质量打印件生成全攻略与工具实践

从网页到PDF:高质量打印件生成全攻略与工具实践

1. 项目概述&#xff1a;从“网页打印件”到高效信息留存每次在网上冲浪&#xff0c;看到一篇干货满满的文章、一个设计精良的教程&#xff0c;或者一份重要的在线文档&#xff0c;你是不是都有一种冲动——把它“保存”下来&#xff1f;收藏夹固然方便&#xff0c;但总感觉不够…

2026/8/26 9:06:51 阅读更多 →
ESP32+HB100多普勒雷达测速系统:从原理到实战

ESP32+HB100多普勒雷达测速系统:从原理到实战

雷达测速这活儿&#xff0c;听着挺硬核&#xff0c;其实动手做起来比想象中接地气。我前后折腾了大半个月&#xff0c;从只知道“雷达测速是警察用的”到能自己搭一套实时监测路上车辆速度的小系统&#xff0c;中间踩了不少坑&#xff0c;也把原本模糊的原理彻底搞透了。这篇就…

2026/8/26 9:06:51 阅读更多 →
深入解析Cortex-M3调试系统:从硬件断点到性能追踪的实战指南

深入解析Cortex-M3调试系统:从硬件断点到性能追踪的实战指南

1. 项目概述&#xff1a;深入Cortex-M3调试系统如果你正在用STM32或者类似的Cortex-M3内核芯片做开发&#xff0c;大概率遇到过这样的场景&#xff1a;程序跑飞了&#xff0c;停在某个奇怪的地方&#xff0c;单步执行时变量值莫名其妙地变化&#xff0c;或者更糟&#xff0c;直…

2026/8/26 9:06:51 阅读更多 →
深入解析Xilinx 7A50T FPGA引脚功能:从电源架构到配置实战

深入解析Xilinx 7A50T FPGA引脚功能:从电源架构到配置实战

1. 项目概述&#xff1a;为什么需要深入理解7A50T的引脚&#xff1f;在硬件设计领域&#xff0c;尤其是FPGA&#xff08;现场可编程门阵列&#xff09;的应用中&#xff0c;芯片的引脚定义图&#xff08;Pinout&#xff09;和功能详解&#xff0c;其重要性不亚于建筑师手中的施…

2026/8/26 9:06:51 阅读更多 →
全开源H5十四合一代付系统源码解析与二次开发部署教程

全开源H5十四合一代付系统源码解析与二次开发部署教程

简介&#xff1a;代付系统作为平台批量打款的核心中间件&#xff0c;是电商结算、佣金发放、供应链付款等场景不可或缺的技术基础设施。它通过统一封装多个支付通道的接口抽象&#xff0c;解决异构上游协议差异&#xff0c;并借助订单状态机保障资金流转可靠性。一套全开源、可…

2026/8/26 9:06:51 阅读更多 →
AI驱动网络钓鱼攻击的防御策略:从技术原理到实战指南

AI驱动网络钓鱼攻击的防御策略:从技术原理到实战指南

1. 项目概述&#xff1a;当钓鱼攻击披上AI的“新衣” 最近几年&#xff0c;网络安全圈里一个老生常谈的话题——“网络钓鱼”&#xff0c;正在经历一场静默但深刻的“工业革命”。过去&#xff0c;我们识别钓鱼邮件&#xff0c;很大程度上依赖于一些“粗糙”的痕迹&#xff1a;…

2026/8/26 9:05:47 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要&#xff1a; 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数&#xff08;random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录&#xff1a; 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中&#xff0c;主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段&#xff0c;转入了政务服务的常态化落地应用&#xff1b;在实际使用过程中&#xff0c;它能自主理解办事需求、辅助完成填报申报、开展材料预审&#xff0c;并联动多个系统协同作业&#xff0c;真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态&#xff0c;宏观上观察到的光是由无数个微观的光量子组成的&#xff0c;每个光子在产生的瞬间&#xff0c;其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前&#xff0c;在微观层面&#xff0c;每个光量子的运动轨迹是以波函数所展现…

2026/8/25 3:38:12 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”&#xff0c;而是SIP会话的动态重定向你有没有遇到过这样的场景&#xff1a;客服坐席A正在和客户通电话&#xff0c;突然需要把这通对话无缝转给专家坐席B&#xff0c;客户完全感知不到中间的断连——既没听到忙音&#xff0c;也没被要求重新拨号…

2026/8/25 3:38:18 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack&#xff1f;如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法&#xff0c;那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 3:38:23 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速&#xff1a;macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南&#xff1a;3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump解密工具帮你轻松解决这个困…

2026/8/25 10:31:12 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片&#xff1a;为英语学习 App 打造桌面级学习助手适用平台&#xff1a;HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0&#xff08;API 26 Beta&#xff09;新增了 AgentCard 智能体卡片能力&#xff0c;这是继 HMAF&#xff08;鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →