首页 / 资讯中心 / 文章详情

鸿蒙Share Kit视频分享实战:ShareData构造、MIME匹配与文件清理避坑指南

鸿蒙Share Kit视频分享实战:ShareData构造、MIME匹配与文件清理避坑指南 ★ FEATURED ARTICLE
做鸿蒙开发这几年我最大的感触是最容易被低估的不是复杂业务而是那些表面上调一下接口就完事的系统能力。Share Kit就是最典型的例子。很多开发者第一次接触它以为不过就是拉起一个系统分享面板传个字符串或者图片完事儿。但当你真的要把一段视频分享出去才会发现里面的门道比想象中多得多。这篇是鸿蒙学习实战之路-Share Kit系列的第5篇主题是分享视频内容。整个系列规划了17篇从最简单的文本分享一步步深入到复杂业务场景。前4篇已经覆盖了分享面板的拉起、文本分享、图片分享和回调处理如果你跟着走过一遍这篇视频分享就是很好的进阶节点。文章重点解决三个问题视频分享和文本、图片分享有什么本质差异怎么正确构造视频分享用的ShareData并拉起系统分享面板实操过程中常见的坑怎么避开。适合有一定ArkTS基础、正在做鸿蒙应用开发、需要给应用接入系统分享能力的同学阅读。1. 项目概述为什么视频分享值得单独写一篇1.1 Share Kit 到底解决了什么问题在扣代码细节之前先把Share Kit的定位说清楚。它是HarmonyOS系统层的分享能力框架核心职能用一句话概括就是把内容从应用A送到应用B。和传统做法里自己构造一个Want、直接指定目标应用拉起相比Share Kit最大的价值在于引入了系统级的分享面板也就是用户点分享按钮后看到的那个弹出层。这个面板会自动聚合系统里所有声明了对应分享能力的应用用户不需要关心目标应用是谁、怎么订阅分享内容开发者也不需要自己去维护一份目标应用列表。系统会根据你传入的ShareData里的类型、MIME、URI等信息做匹配筛选把找目标应用这件事下沉到系统层。这也是为什么很多应用宁可放弃自己维护渠道列表也要用系统分享面板——省心覆盖面广还不用小心翼翼去适配各家接口。1.2 视频分享和其他内容分享的差异在哪文本、图片、视频这三类内容在Share Kit里的实现路径完全不一样视频是最考验工程意识的文本没有文件的概念字符串直接丢给面板就行不存在生命周期、文件清理、URI过期这些麻烦事。图片虽然也是文件但体积基本可控本地缓存也好、中转下载也好性能压力都不大。视频就完全不同了一段手机录制的1080P视频动辄几十MB传出去要时间接收方打开要时间。如果只是传一个在线链接还好说一旦要分享本地视频文件就必须考虑文件URI合法性、MIME类型是否匹配、接收方是否支持当前编码格式、分享完成后临时文件到底要不要清理、什么时候清理。这些话不是危言耸听。我在真机调试时就陆续遇到过分享面板里目标应用不显示、分享完成后对方打不开视频、文件被提前删除导致对面拉取失败等一串问题。这些问题如果不在设计阶段想清楚后面排查起来会非常痛苦这也是我建议你把这篇单独拎出来看的原因。2. 核心设计想清楚分享链接还是分享文件2.1 ShareDataKind 两种类型的本质差异Share Kit里有个关键枚举叫ShareDataKind决定你要分享的数据形态。实际开发中最常用的是两种ShareDataKind分享内容ShareData里的关键字段适合场景LINK一条URL链接url、title、description视频已在线分享一个播放页地址FILE一个真实文件uri、mimeType、thumbnail分享手机里的本地视频文件LINK类型对分享方来说是最轻量的你只需要一个视频播放页的url系统面板会把链接原样带给目标应用接收方靠自己的浏览器或者内置WebView打开。优点是快、不用管文件存储缺点也很明显如果接收方网络不好、或者目标应用并不想直接解析链接用户体验就很一般。FILE类型才是真正意义上的把视频给对方ShareData里要传一个合法URI接收方拿到文件URI后去读取、拷贝、播放。像是把文件实体交到对方手里离线也能看。2.2 视频分享场景里怎么选我的建议是判断你的视频当下能不能被接收方直接访问。如果视频已经上传到某个公网地址分享LINK最稳妥。别把播放页URL糊弄成一个裸文件地址那样目标应用可能无法渲染。最好传一个带播放器的页面地址并配好标题和描述方便接收方直接展示卡片。如果是本地视频比如用户刚录制完、从相册选中了一段素材那就必须用FILE类型。本地文件不可能通过链接被对方访问你要做的是把视频文件URI、MIME类型、缩略图都准备好交给系统分享面板。有些场景是混合的视频文件在服务端但下载下来可能更大很多短视频应用选择分享LINK而工具类、办公类应用更经常分享FILE。没有绝对对错看业务形态。但有一点确定本地视频一定用FILE在线视频优先LINK这条原则基本不会错。3. 实操构造ShareData并拉起系统分享面板3.1 怎么准备一个可被分享的视频文件URI先说结论如果走FILE分享最省心的方式是先把视频落盘到应用自己的cache目录用file://协议构造URI传给Share Kit。为什么是cache目录而不是外部媒体库两个原因。第一cache目录是应用私有的不需要额外申请媒体库读取权限也不牵扯用户授权弹窗第二file://协议URI对大多数接收方应用都很友好解析成本低。媒体库路径在部分场景下需要配合photoAccessHelper做授权链路更长不适合当默认方案。把视频写到cache目录的核心代码很直白import { fileIo as fs } from kit.CoreFileKit; import { common } from kit.AbilityKit; let context getContext(this) as common.UIAbilityContext; // 假设你已经拿到了视频字节流或源文件路径 // 先把视频复制到 cacheDir得到一个干净的副本 async function prepareVideoFile(sourcePath: string, targetName: string): Promisestring { const targetPath ${context.cacheDir}/${targetName}; // 如果目标已存在可以覆盖式打开 const sourceFile fs.openSync(sourcePath, fs.OpenMode.READ_ONLY); const targetFile fs.openSync(targetPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE | fs.OpenMode.TRUNC); fs.copyFileSync(sourceFile.fd, targetFile.fd); fs.closeSync(sourceFile.fd); fs.closeSync(targetFile.fd); return targetPath; }这里有个细节一定要养成习惯用完立即关闭文件描述符。鸿蒙上文件句柄是稀缺资源尤其是在循环分享多条视频的场景里不关fd很快就会触发EMFILE文件描述符耗尽错误。我见过不止一个项目在批量处理视频时卡死最后查下来就是fd泄漏。3.2 ShareData 的关键字段怎么填视频分享的ShareData核心是这几个字段import { share } from kit.ShareKit; const shareData: share.ShareData { kind: share.ShareDataKind.FILE, uri: file://${videoPath}, // 必须指向视频文件的file:// URI mimeType: video/mp4, // 必须告诉接收方这是什么格式 title: 旅行vlog-青海湖, // 建议填面板里展示的标题 description: 一段4K视频记录环湖骑行, // 可选补充描述信息 thumbnail: file://${thumbPath} // 建议填预览缩略图URI };字段逐个说kind固定share.ShareDataKind.FILE别传错成LINK否则系统面板会把你当链接处理文件URI根本发不出去。uri必须是可访问、有效的文件URI。我建议统一拼成file://前缀加绝对路径的格式。如果路径里有中文、空格要注意URI编码问题否则接收方解析会出幺蛾子。mimeType这是视频分享最容易翻车的一个字段。很多开发者只填video/mp4但如果你的视频是MKV或WebM封装接收方应用压根不会出现在分享面板里。后面第4节我会给完整对照表。thumbnail缩略图URI指向一张图片文件。没有缩略图分享也能跑但面板里展示效果会很干接收方可能只显示一个默认文件图标用户点击意愿直接下降。3.3 完整分享流程封装有了数据拉起系统分享面板就三件事创建SystemShareController实例、调用show()方法、处理异常。我习惯封装成一个独立方法方便业务侧复用import { share } from kit.ShareKit; import { common } from kit.AbilityKit; async function shareVideoFile(context: common.UIAbilityContext, videoPath: string, videoName: string) { const shareData: share.ShareData { kind: share.ShareDataKind.FILE, uri: file://${videoPath}, mimeType: video/mp4, title: videoName, description: 来自我的应用分享, thumbnail: file://${context.cacheDir}/video_cover.jpg }; const shareController new share.SystemShareController(); try { await shareController.show(context, { shareData: shareData, shareSource: { title: 分享视频, appIcon: context.applicationInfo.icon } }); console.info(分享面板拉起成功); } catch (err) { console.error(分享面板拉起失败code: ${(err as BusinessError).code}, message: ${(err as BusinessError).message}); } }几点实操心得show方法传入的context必须是当前活跃的UIAbilityContext。如果你在onCreate阶段或者后台任务里调用系统没法把分享面板挂到正确的窗口层级上大概率直接抛错。建议等页面onPageShow之后再去触发。shareSource里的title是面板顶部展示的应用名appIcon是图标。这两个字段不传也能跑但面板会显得很糙建议始终带上。调用时机——分享面板是模态弹出的所以不要在弹面板之前做一些阻塞性操作比如大文件的同步拷贝。我习惯先把视频复制到cache目录、确认文件可读再调show方法避免用户在面板上等半天。3.4 缩略图不传行不行能跑但不推荐。系统分享面板在有缩略图时会展示一个视频预览图卡片没有缩略图时只有一条干巴巴的文本信息观感差距非常大。生成缩略图的常见办法有两个。一是视频在业务侧本来就有封面图比如录屏功能的首帧、编辑器的封面裁剪直接复用最方便。二是视频素材没有封面那就需要从视频里截一帧。在鸿蒙上可以借助视频解码能力取首帧并转成PixelMap再压缩成jpg写进cache目录。如果只是做个最小可用版本也可以临时渲染一张纯色占位图但接收方体验会打折。我实际开发中更推荐的方式是在用户录制或选择视频后立刻异步生成封面别等到用户点分享那一刻才现场截帧避免分享动作卡顿。4. 细节打磨MIME类型、资源清理与相册场景4.1 MIME类型对照表与匹配原理MIME类型是分享面板做应用筛选的硬条件。系统面板会拿你的mimeType去和所有应用声明的支持类型做匹配匹配上的才会出现在面板列表里。填错了后果就是目标应用直接不出现或者面板是出现了但点进去全部置灰。常见的视频封装格式和对应MIME类型我整理了一张表建议直接存下来视频封装格式MIME类型MP4最常见video/mp4MKVvideo/x-matroskaWebMvideo/webmAVIvideo/x-msvideoMOVvideo/quicktimeTS / M2TSvideo/mp2t3GPvideo/3gppFLVvideo/x-flv有个场景容易踩坑你分享的是一个视频文件但接收方应用声明的是video/*通配这种一般能匹配上但有些应用只声明了video/mp4那你的MKV视频就算文件没问题对方也不会出现在面板里。这是生态之间的兼容性问题你没法替目标应用做主能做的是在分享前检测一下文件扩展名、填对mimeType把问题范围缩到最小。4.2 分享完成后的临时文件清理策略视频文件体积大如果每次都往cache目录丢副本、又不清理过几天磁盘就被撑爆了。但清理时机得想清楚不能一股脑分享完就删。关键点在于分享面板把数据交给目标应用后目标应用可能立即拷贝也可能异步拉取。你要是同步删除接收方可能报文件不存在你要是一直不删又会堆积垃圾。我总结出来的策略是三层分享前把视频复制到独立子目录比如cache/share_cache/下加时间戳命名。分享完成后延迟一段时间再清理比如页面onPageHide之后延迟5到10分钟。启动应用时扫一遍share_cache目录清掉超过24小时的残留文件。这样可以兼顾接收方来得及拉取和目录不堆积。延迟清理可以用setTimeout配合页面生命周期或者干脆做成轻量的后台任务按项目体量取舍。4.3 从相册选视频再分享的完整链路很多业务场景不是自己生产的视频而是用户从相册里选了一条。这时候链路多了两步调起相册选择器拿到选中视频的URI然后才能构造ShareData。在鸿蒙上相册选择推荐用PhotoViewPicker它会返回一个URI列表。拿到URI后注意区分来源如果URI是file://开头说明能直接访问可以少一步拷贝如果是datashare://这种媒体库URI部分目标应用可能没有权限直接读建议还是先拷贝到cache目录再分享稳一点。import { photoAccessHelper } from kit.MediaLibraryKit; async function pickAndShareVideo(context: common.UIAbilityContext) { const picker new photoAccessHelper.PhotoViewPicker(); const result await picker.select({ MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, maxSelectNumber: 1 }); const photoUri result.photoUris[0]; // photoUri可能是datashare://也可能是file:// // 稳妥起见先转存到cache再分享 const localPath await copyToCache(context, photoUri); await shareVideoFile(context, localPath, selected_video.mp4); }转存这一步逻辑和第3节里的prepareVideoFile基本一致只是源路径换成photoUri。注意在调用前判断URI协议用不同的打开方式去读避免一股脑openSync导致类型不匹配报错。5. 常见问题排查与避坑实录5.1 高频问题速查表这里把我在真机和模拟器上踩过的坑整理成表格后面排查问题时可以直接对着找现象可能原因解决办法分享面板没有出现任何目标应用MIME类型不匹配面板筛不出接收方检查视频扩展名和mimeType是否对应改成video/mp4或多试video/*分享按钮点了没反应无报错context不合法比如传了ApplicationContext确认传的是当前页面的UIAbilityContext接收方点了视频打不开视频编码不兼容或文件没有完整写入检查编码格式确认分享前文件已关闭fd、大小符合预期分享完成后目标应用报文件不存在临时文件被提前删除延迟清理策略至少保留5分钟以上面板能拉起但对方收到的体积为0打开了文件但没写完就close或拷了空文件检查写入流程分享前校验文件大小大于0大视频分享时界面卡顿准备数据过程阻塞了主线程转存、截缩略图放子线程或使用TaskPool5.2 两个最容易被忽略的深坑第一个深坑是URI的协议前缀。Share Kit对URI格式很敏感。我遇到过一个情况从某个第三方SDK拿回来的路径是/data/storage/el2/base/...这种纯路径直接拼到ShareData里分享面板直接报参数错误。正确做法是统一转成file://协议。同理如果路径带特殊字符要先做URI编码否则接收方解析会中断。第二个深坑是分享面板show方法返回后的处理。show返回不代表分享成功它只表示面板正常展示或者关闭。分享真正有没有送达目标应用要靠业务侧自己的回调或者应用间返回的数据去判断。很多新手误以为await show成功就是分享成功了然后马上删文件结果就是开头说的接收方打不开。这块建议业务重要时把文件保留窗口拉到足够长业务不敏感时直接按延迟清理策略走。5.3 我自己的调试习惯最后分享一个很实用的调试习惯。分享面板这种系统UI在模拟器上表现和真机差距很大尤其是MIME匹配和URI解析模拟器环境过于宽松经常模拟器能出、真机收不到。所以我做分享功能时有一个固定流程第一先用真机把分享面板调到开发模式打开日志过滤观察系统打印的分享数据日志确认uri、mimeType、title都按预期传过去了。第二准备一个最小复现工程只留一个按钮、一个固定视频文件先调试最基本的FILE分享确认面板列表正常了再往里面叠加相册选择、缩略图生成等复杂逻辑。第三每次改完代码都清一次cache目录确保不会因为历史残留文件影响了分享结果判断。按这个流程走了几轮之后视频分享的稳定性会明显上一个台阶。说实话视频分享的代码量并不大但每个环节都藏着小坑。把ShareData的字段语义吃透、把文件生命周期理清楚、把MIME类型填对这个功能就算做扎实了。要是你在项目里也遇到过我上面提到的某个坑可以直接对照速查表排查没有遇到的也建议提前把延迟清理策略设计进去别等到线上爆了盘再来补。
阅读完成 · 觉得有帮助?
咨询建站