☰
HarmonyOS 7 Media Library Kit:视频选取URI授权范围校验【鸿蒙心迹】
2026/10/11 1:26:31 网站建设 项目流程

相册选择器返回了视频URI,页面就立刻把这些URI扔进媒体处理队列,界面看起来没有什么问题。等应用接入第三方素材列表、历史草稿恢复和H5素材选择后,事情就不一定这么简单:有的URI根本不来自用户本次选择,有的虽然曾经在选择结果中出现,重新查询时已经找不到对应资源。只判断字符串不为空就开始处理,容易把“持有一个URI”误当成“具备当前读取资格”。

这篇使用MediaScopeLedger做一个范围很窄的准入检查。它不做视频抽帧,也不讨论封面解码性能,而是把本次Picker明确返回的URI集合当成一份临时授权范围,再把候选资产查询结果和最低限度的元信息校验分开。固定任务号URI-1010-17,批次clip_ingest_06,主页面AssetGatePage,诊断页面AssetAuditPage。所有URI均为夹具中的脱敏业务ID,不是实际设备URI。系统Picker和真实图库查询在本轮均标记NOT_RUN。

一、问题不是“能不能拿到视频”,而是谁给了这条URI资格

用户通过系统PhotoPicker选择素材,与应用接到某个组件随意发送的字符串,是两件不同的事。前者存在明确的人机操作边界:用户在系统页面内决定选择哪些媒体;后者只是一次普通的数据传递。真正用于读取相册的能力需要结合图库接口、用户本次选择、URI可访问条件及应用权限判断,而不是凭file://、content://或其它前缀做猜测。

这也是为什么文章专门把“URI范围”单独拿出来讨论。假设编辑器展示六条候选视频,其中五条来自本次系统选择结果,一条来自旧草稿里记录的标识。如果所有候选一起交给媒体服务,那条旧草稿URI也许会被当成已经被用户授权。更糟糕的是,它可能指向用户根本没打算提供给当前编辑任务的其它素材。判断是否属于本次Picker集合,必须发生在申请进一步访问之前。

Demo把六条候选固定为V01至V06,V01到V04在范围中并且可以通过后续资产存在性检查;V05来自非本次Picker集合,判为OUT_OF_SCOPE;V06属于本次选择结果,但查询不到相应PhotoAsset,判为ASSET_MISSING。所以候选数六、Picker集合数五、成功准入四、范围外一、资产失效一,最终业务状态ASSET_HOLD。这里的四项通过是本地规则中的通过,不表示真机读取了四个视频。

我更关注失败之后的行为。V05不应该被“自动补授权”,也不能为了渲染预览而临时跳过集合校验;V06则不应反复在后台请求同一URI直到某次偶然成功。前者需要用户重新明确选择,后者要提示资源可能不可用,允许用户重新选择或从候选列表移除。两种失败结果虽然在UI上都可以标红,其工程处理途径却完全不同。

二、选择器返回结果是一次性输入,不是长期通行证

华为 Media Library Kit 当前推荐使用photoAccessHelper.PhotoViewPicker。官方参考资料同时明确指出旧版@kit.CoreFileKit下的picker.PhotoViewPicker已逐步弃用,不宜拿老范例当成唯一实现。对于只需要用户指定几段视频的编辑器,系统Picker提供的受控选择方式通常比申请大范围相册读取权限更合适。

不过,Picker只解决“用户本轮选了什么”,并没有替编辑器承诺URI永久有效。用户可以取消、系统可能因文件变更导致查询失败,应用也可能因为生命周期切换而失去之前保存的上下文。一个来自上次编辑会话的URI,即使曾经合法,也不应自动继承到新的编辑任务。这个判断要靠应用建立本轮集合和批次标识,不能要求某个字符串自己携带永久授权证明。

在演示模型里,PickedUriLedger维护一个只读式快照:批次为clip_ingest_06,被选择的五个业务ID为V01、V02、V03、V04、V06。外部候选的ID只有先映射到一个URI,并且确实存在于快照里,才允许进入图库查询层。业务ID到真实URI的映射只应该存在于应用内部临时上下文,不宜写入日志或发布截图。一个单独的加密或摘要字段也不能自动代表用户授权,最终的权限边界仍由系统与本次选择共同决定。

取消Picker的处理尤其容易写错。如果用户只是关闭系统选择界面,业务层应该保留当前已确认的批次,而不是拿空结果直接覆盖它。重新选择成功以后再创建新的集合,并清理旧集合对应的处理中状态。这样用户对一次选择的主观意思才与应用行为一致。页面返回或前后台切换,也应该明确保留集合的有效期和清理时机,避免会话无限延期。

三、只接收官方返回的photoUris,不从UI直接推导文件路径

第一段代码解决的是系统选择器和业务层之间的数据入口。PhotoSelectOptions约束希望用户看到的视频类型及最多选择数量,结果采用photoUris,而不是自行编造photoAssets这样的字段。这里最多选择五段,是因为本次演示故意把第五段有效Picker结果V06与额外注入的范围外候选V05作对照。真实项目里这个上限可以依据产品需求配置。

// entry/src/main/ets/pages/AssetGatePage.ets — 选择入口节选 import { photoAccessHelper } from '@kit.MediaLibraryKit'; export class PickerEntry { async chooseVideos(): Promise<Array<string>> { const picker = new photoAccessHelper.PhotoViewPicker(); const options = new photoAccessHelper.PhotoSelectOptions(); options.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE; options.maxSelectNumber = 5; const result = await picker.select(options); // 返回的是用户选中的photoUris,不是可以随意拼路径的磁盘文件。 return Array.from(new Set(result.photoUris)); } }

调用示例未把异常直接吞掉。实际上层还要在UIAbility对应的用户交互中触发Picker,并区分“用户取消”“系统调用失败”“返回列表为空”这三种不同情况。本文的固定夹具不曾唤起系统Picker,因此相册授权和API实际返回结构尚需在DevEco Studio目标SDK以及真机上验收。为便于离线对照,图片里的系统Picker:NOT_RUN就是这条明确边界。

与此相关的另一个误区,是对所有返回URI做字符串前缀过滤就称为安全。平台可能对媒体URI采用不同编码与表现形式,直接依赖前缀有兼容性风险;反过来,一条篡改后的字符串也可能复制出合法前缀。我们这里的集合不是利用URI格式识别授权,而是精确比较本轮Picker实际交给应用的完整字符串。只要不是这次集合里的成员,就无法进入下一阶段。

四、准入门禁首先检查集合成员,再查询资源存在性

选取范围属于业务层,资产是否还能查询属于平台层。把这两步合到一个“try一下能不能读”函数中,会模糊失败原因:一个根本没被用户选择的URI与一个已被选择但已失效的URI,最终都可能抛出错误。Demo先由PickedUriLedger判断集合成员,然后才调用MediaAssetLookup。这样范围外候选完全不会触发图库查询,减少不必要的权限探测。

下面这段ArkTS是应用层纯逻辑,使用candidateId表示日志可见的脱敏标识,不包含真实URI。pickerSet里的键来自上一步Picker返回结果所映射的稳定ID。查询函数由平台适配器注入;如果查不到资产,它返回false,不会把无法确认的情况自动提升为VALID。

// entry/src/main/ets/model/PickedUriLedger.ets export type GateState = 'VALID' | 'OUT_OF_SCOPE' | 'ASSET_MISSING'; export class PickedUriLedger { private pickerSet: Set<string> = new Set(); constructor(selectedIds: Array<string>) { selectedIds.forEach((id) => this.pickerSet.add(id)); } async validate(candidateId: string, exists: (id: string) => Promise<boolean>): Promise<GateState> { if (!this.pickerSet.has(candidateId)) return 'OUT_OF_SCOPE'; try { const available = await exists(candidateId); return available ? 'VALID' : 'ASSET_MISSING'; } catch (_) { // 不确定能否查询时,按不可使用处理并保留错误原因。 return 'ASSET_MISSING'; } } }

用Set做判定看起来简单,但它解决了一项重要的责任划分:用户本轮没有挑中的候选,不应被送入系统资产查询。若应用从历史编辑记录或第三方H5接收URI,必须把它们先当作未授权来源,不能为了编辑体验“自动补全”到本次Set中。用户需要重新确认。与此同时,Set只是业务侧的附加约束,并不能代替平台实际检查访问权限。

如果两个不同候选ID映射到同一个URI,也要避免重复计算造成统计虚高。正式实现应该建立URI→稳定候选ID的映射,并为重复提交制定明确政策,例如只保留首次出现并告知用户。由于本次固定夹具不存在重复URI,所以我们没有把重复项塞进六个结果里;这项边界留给回归用例。这样可避免把同一个失败资产重复报为多个缺失问题。

五、系统getAssets查询是另一道关,不能假设“选择即读取成功”

官方“基于系统能力获取视频缩略图”的文档提供了从所选图库URI构造DataSharePredicates、调用PhotoAccessHelper.getAssets()、再获取PhotoAsset的示例。这个过程说明:选择结果和具体PhotoAsset之间仍隔着一层资产查询。这里我们不做后续getThumbnail(),也不分配PixelMap;原因很明确,本篇问题是访问范围和元信息准入,而不是前一篇已经讨论过的封面异步回填或资源释放。

第三段代码展示查询链路里可单独核对的一部分。uri必须由前一步通过Set门禁后提供,不能由外部字符串直接跳到此函数。使用本轮可核对的字段width、height、orientation做基础元信息检查;这些字段在华为媒体缩略图文档的fetchColumns示例中有出现。若查询失败或没有对应对象,不把它当成有效资产。

// entry/src/main/ets/service/MediaAssetLookup.ets — 适配器核心 import { photoAccessHelper } from '@kit.MediaLibraryKit'; import { dataSharePredicates } from '@kit.ArkData'; export async function lookupSelectedAsset( helper: photoAccessHelper.PhotoAccessHelper, uri: string ): Promise<photoAccessHelper.PhotoAsset | undefined> { const predicate = new dataSharePredicates.DataSharePredicates(); predicate.equalTo('uri', uri); const result = await helper.getAssets({ predicates: predicate, fetchColumns: ['width', 'height', 'orientation'] }); try { return await result.getFirstObject(); } catch (_) { return undefined; // 查询为空或失效;上层记录ASSET_MISSING } }

以上函数并未声称已经完成所有媒体类型、内容安全和生命周期验证。正式工程还需要按所用SDK核对FetchResult的关闭/释放方式,避免资产查询结果对象长时间持有;并依据真实产品明确元信息准入条件,如文件尺寸、播放格式、视频时长与设备解码支持。本文不把width>0写成能播放的保证,也不把“扩展名是mp4”写成已通过文件内容验证。这里的资产查询门槛仅是第一步。

图二是独立生成的DevEco风格拟真演示,左边文件树显示AssetGatePage.ets、AssetAuditPage.ets、PickedUriLedger.ets与MediaAssetLookup.ets,中间突出Set范围判断及Picker的photoUris入口,右侧模拟器固定列出六条候选和准入结果。底部HiLog里的URI-1010-17输出属于固定夹具,不来自真实图库服务。系统选择器和系统资产查询继续保持NOT_RUN,不能因为UI已有四张预览画面就声称系统授权完成。

六、六条样例如何产生4/1/1,而不是随便摆三个统计数

当前夹具里,Picker集合是V01、V02、V03、V04、V06共五项。V01“湖面日出”、V02“城市天际线”、V03“秋日公路”、V04“海岸日落”各自在本地存在性表里得到true,因此进入有效列表。V05“私人片段”即使被外部组件传到了候选队列,由于不在Picker集合里也不允许执行lookupSelectedAsset,立即标记OUT_OF_SCOPE。V06“损坏视频”属于Picker集合,但是存在性表给出false,状态ASSET_MISSING。所以总数六条,集合数五,通过四、范围外一、资产失效一。

这些名称与文件大小仅用于设计移动界面;它们不对应某个用户真实视频,也不意味着媒体容器格式已经验证。主页面使用ASSET_HOLD表示“整批仍有两项不能进入后续处理”,并不等于成功四项也全部作废。对于编辑器来说,可以保留已经通过的四条,将两项问题分别展示给用户,让用户有机会重新选择或移除。比起把整个任务显示为“未知错误”,这种输出更利于后续产品决策。

状态必须来自同一份已冻结的审计快照。如果Picker选择结果刚发生变化,旧的诊断页还在展示上一次的六条用例,应在UI中标明批次,不要让它假装代表新一轮选取。真实工程可加入selectionRevision、时间戳和上下文所有者,将缓存报告与本轮集合关联。否则开发者很容易把“V06在上一轮缺失”的日志错误归因到今天选的另一个视频。

主运行示意图刻意把“六条候选”和“Picker返回五项”并列显示,让读者立即看到两者不是同义词。右下角的V06不显示可用封面,不是因为系统一定无法解码,而是本地夹具模拟查询缺失。V05显示红色范围外警示,提醒开发者它压根不应访问图库。页面标注NOT_RUN,表示尚未调用系统Picker和实际getAssets。这一层诚实的状态标注比一张绿勾满屏的演示更有参考价值。

七、诊断不能记录真实URI,却要能复盘每个拒绝原因

AssetAuditPage面向开发与测试而非普通用户。它展示V01至V06的稳定业务标识、范围检查结果、资产查询结果以及最终状态,而不是原始URI字符串。范围外项V05的资源状态为N/A,原因是根本没有发生查询;V06属于范围内,但资产查询为空,因此明确标为ASSET_MISSING。如果诊断表里给V05写了“文件不存在”,就是把两个责任边界混在了一起。

四条固定日志用于展示这套判定顺序:10:17:11加载本地Picker集合五项;10:17:12拒绝V05为OUT_OF_SCOPE;10:17:13模拟V06查询不存在;10:17:14汇总通过4、范围外1、缺失1并保持ASSET_HOLD。其中所有时间都是统一的数据契约,不是程序执行耗时,也不是实际图库查询时间。正式产品的日志记录还要按资源隐私策略进行最小化,避免通过文件名、拍摄地点或媒体URI泄露用户信息。

这里值得警惕的还有一个常见“修复”:开发者发现V06查不到,就临时申请更大范围相册权限以求让它成功。权限扩大必须有明确业务理由和用户交互,不能把一次资源已失效的问题直接推成权限不足。更稳妥的做法是先复核该URI是否由本次Picker产生、是否仍可查询、是否本来被用户删除或移动。只有完成归因,才能决定是否需要改变产品授权路径。

对于上架和隐私说明而言,系统Picker访问用户选定内容,与申请全量图库权限的语义也不同。文章只讨论API和行为边界,不会杜撰应用市场一定允许、一定拒绝或一定免审核的规则。涉及权限声明时应回到最新官方申请说明和目标业务场景逐项核对;不能以此Demo的一组固定数据替代审核政策判断。

八、真正的回归检查要让“允许读取”变得可证伪

若有真实设备,可先在UIAbility中发起Picker,选择五段视频,保存本次URI映射而不记录原文。随后从可信UI尝试查询其中四段,确认资产可访问并能拿到所需元信息。再从一个独立的外部输入通道注入V05,检查它是否在业务层就被拒绝,而不是已经触发图库查询。接下来让用户在相册中删除或修改一项已选素材,重新检查V06的失效路径;最后取消新一轮Picker,确认之前的集合不会被空结果覆盖。

设备验证还应覆盖从手机返回桌面、后台恢复、跨页跳转、批量编辑重新打开、模拟器与真机的能力差异。对每一种场景记录当前批次、Picker返回的候选数量、实际查询次数和拒绝原因,不把所有失败都写成permission denied。如果出现真实权限错误,需要保留错误码用于定位,但日志仍不能记录完整隐私路径。对于处于“范围内但失效”的项目,用户应有重新选择按钮,而不是陷入不可结束的重试循环。

另外,还要特别处理授权快照的结束时机。应用无法通过维护一个Set来延长系统授权,Set的作用只是收紧本地读取资格;当页面离开或编辑事务结束,内存中不再需要的URI映射应清理,后台异步工作不应拿旧批次数据继续读取。若希望长期恢复素材工程,应依据系统能力另行设计持久访问方案,不能简单把本次返回的URI写入Preferences就假设下次一定还能用。

四项通过并不能代替后续视频播放、转码或缩略图生成测试。这个Demo没有打开视频文件,没有抽帧,也没有创建PixelMap,所以不会虚构内存峰值或解码耗时。如果以后真正接上取图功能,应在范围准入成功之后另建独立任务,接收经验证的PhotoAsset,并为后续对象建立资源释放协议。把准入与处理分层,才有机会在出错时判断到底是授权、资产、解码还是UI回填出了问题。

九、结果与后续工程取舍

目前已经确定的是一套可复核的业务策略:六个候选中五个属于Picker固定集合,四个模拟资产存在、一个范围外、一个资源缺失,最终状态ASSET_HOLD。纯数据夹具可以验证这个结果,生成的UI也沿用同一任务号和状态。没有确定的是系统Picker真实返回、PhotoAccessHelper真实资产查询、权限变化、媒体播放兼容性和真机性能,因为这些工作在本轮尚未执行。

如果只能为这篇Demo保留一条工程原则,我会保留**“URI来源资格”和“媒体资产可用性”是两道独立判断**。前者要求尊重用户本次选择的边界,后者要求对资源现在是否存在作出客观判断。既不能让任意URI因为格式像就被提升为已授权,也不能把一次不存在误写成已经完成系统权限审查。等到有真实设备数据时,再逐步替换NOT_RUN,比一开始就声称流程闭环更有价值。

十、官方资料与验证边界

华为 Media Library Kit 视频缩略图示例(2026-09-14):https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/video-thumbnail-system

华为 Picker API 参考文档(2026-09-08,明确PhotoViewPicker弃用迁移与photoUris相关边界):https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-file-picker

本文的选择列表、六项资产、全部时间、状态与图片均为固定输入模拟数据。源码展示真实公开接口的接线意图与应用层校验机制,尚未完成DevEco Studio编译、系统Picker授权、PhotoAsset查询、设备视频读取或AppGallery审核,不能把模型通过冒充成设备实测。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询