查看: 253|回复: 3

鸿蒙 PhotoViewPicker 图片选择 UTS 插件踩坑

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在鸿蒙项目里做头像上传、朋友圈发图、商品评价晒图,都会遇到从相册选图并上传的需求。原文作者最初使用 uni-app 的 uni.chooseImage,但在鸿蒙上行为不一致,有时选完图片拿不到正确路径;改为通过 UTS 插件直接调用鸿蒙原生 photoAccessHelper.PhotoViewPicker 后,返回标准 media:// URI,稳定可靠。下面围绕这个插件的 API 调研、封装实现和四个踩坑点展开。

一、需求与 API 调研

需求是支持多选、最多 9 张,符合朋友圈等常见场景。uni.chooseImage 在鸿蒙上偶尔返回临时路径,后续处理麻烦;UTS 插件直接调原生 API,返回的 media:// URI 既可用于 image 组件展示,也可传给后端上传。

鸿蒙的图片选择能力在 @kit.MediaLibraryKit 的 photoAccessHelper 模块中。核心步骤只有三步:构造 PhotoSelectOptions、创建 PhotoViewPicker、调用 select。关键配置包括:MIMEType 设为 IMAGE_TYPE 时只显示图片;maxSelectNumber 在插件中默认 9,系统最大支持 500,不设置时默认 50;isPhotoTakingSupported 设为 true 后,选择器里会显示拍照按钮,拍完自动加入选择结果。

需要特别注意,select 返回的是 media:// 开头的 URI,不是 /data/storage/ 这类文件系统路径。这种 URI 只能用于读取,不能直接当文件路径用;如果要读取文件内容,需要用 fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY) 打开,再通过 fd 读取字节流。用户取消选择时,select 会走 catch 分支,错误码为 13900006,表示用户主动取消,不是真正的错误。插件里把它当成 fail 回调返回,错误信息为“用户取消选择”。

二、插件封装结构

插件代码放在 uni_modules/md-image-picker 目录下。interface.uts 定义 ImagePickerOptions、ImagePickerResult、PickImage 以及错误码 9210001;unierror.uts 定义 UniErrorSubject 和 ImagePickerErrors,错误码 9210001 对应“选择图片失败”;app-harmony/index.uts 实现 pickImage;示例页面在 pages/image-picker/image-picker.uvue。

核心实现可以概括为:读取 options.maxCount 和 options.canTakePhoto,构造 PhotoSelectOptions,设置 MIMEType、maxSelectNumber、isPhotoTakingSupported,然后创建 PhotoViewPicker 并调用 select。成功时把 result.photoUris 放入 uris 返回;失败时区分错误码 13900006 和其他异常。
  1. const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
  2. photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
  3. photoSelectOptions.maxSelectNumber = options.maxCount ?? 9;
  4. photoSelectOptions.isPhotoTakingSupported = options.canTakePhoto ?? true;
  5. const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
  6. photoViewPicker.select(photoSelectOptions).then((result: photoAccessHelper.PhotoSelectResult) => {
  7.   const res: ImagePickerResult = {
  8.     uris: result.photoUris,
  9.     errMsg: 'pickImage:ok'
  10.   };
  11.   options.success?.(res);
  12.   options.complete?.(res);
  13. }).catch((error: BusinessError) => {
  14.   if (error.code == 13900006) {
  15.     const err = new ImagePickerFailImpl(9210001);
  16.     err.errMsg = '用户取消选择';
  17.     options.fail?.(err);
  18.     options.complete?.(err);
  19.   } else {
  20.     const err = new ImagePickerFailImpl(9210001);
  21.     err.errMsg = '选择图片失败: ' + error.message;
  22.     options.fail?.(err);
  23.     options.complete?.(err);
  24.   }
  25. });
复制代码

示例页面通过 pickImage 传入 maxCount、canTakePhoto、success、fail,成功后把 res.uris 赋给 resultUris,用 image 组件按 aspectFill 展示,并用 toast 提示选择数量。调用方如果需要上传,需要自己处理 media:// URI 的读取。

三、四个踩坑点

坑一:MIMEType 不设置会导致视频混入。原文作者最初以为默认只选图片,结果选择器里同时出现视频,用户容易误选。PhotoViewMIMETypes 有三个值:IMAGE_TYPE 只显示图片,VIDEO_TYPE 只显示视频,IMAGE_VIDEO_TYPE 图片和视频都显示;不设置时默认是 IMAGE_VIDEO_TYPE。插件固定用 IMAGE_TYPE,因为定位是图片选择插件。

坑二:用户取消选择走 catch 分支。点击选择器右上角取消后,会进入 catch,错误码 13900006。这个设计反直觉,但鸿蒙把用户取消当成异常流程处理。插件将其作为 fail 回调返回,调用方可根据错误信息判断是取消还是其他错误。

坑三:返回的 media:// URI 不能直接当文件路径。原文作者曾把 URI 直接传给 fileIo.openSync,结果报“不支持的协议”。这种 URI 可以直接用于 image 组件的 src 属性展示图片;要读取文件内容上传服务器,必须用 fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY) 打开后通过 fd 读取。插件只返回 URI,不做文件读取。

坑四:maxSelectNumber 超过 500 会报错。系统最大支持 500,原文测试时随手写 1000,导致选择器都打不开。插件默认 9,一般不会超过,但调用方传入超大值时鸿蒙会直接报错。原文指出插件没有做上限校验,实际项目建议加 Math.min(options.maxCount, 500) 保护。

总结:图片选择插件的核心就是 PhotoSelectOptions、PhotoViewPicker、select 三步。真正需要花时间理解的是 media:// URI 的类型和使用限制,以及用户取消选择时的错误码处理。把 UTS 插件封装在 uni_modules/md-image-picker 下,示例页面放在 pages/image-picker/image-picker.uvue,调用方按需处理 URI 读取和上传链路即可。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙 PhotoViewPicker 图片选择 UTS 插件踩坑

这篇整理挺实用的,尤其是把 PhotoViewPicker 的默认行为和几个反直觉的点讲清楚了。MIMEType 不设置会默认 IMAGE_VIDEO_TYPE、导致视频混进来,这个坑确实很容易中,插件固定成 IMAGE_TYPE 也符合图片选择器的定位。用户取消走 catch、错误码 13900006 这点也很关键,如果不单独处理,调用方很容易把取消当成真正的失败弹一堆错误提示,插件里转成 fail 并给出“用户取消选择”算比较友好。 media:// URI 不能直接当文件路径用这一点也值得在插件文档里重点标出来,毕竟很多人从 uni.chooseImage 迁过来会习惯性拿返回值去 openSync 或者直接传给上传接口。按帖子里说的,展示用 image 的 src 没问题,真要读内容还得 fileIo.openSync 加 READ_ONLY 再走 fd 读字节流,调用方上传前得自己补这一步。另外 maxSelectNumber 默认 9、系统上限 500、isPhotoTakingSupported 打开拍照按钮这些配置项,建议在示例页面里都带上注释,后面维护的人会省事很多。 帖子最后关于读取文件内容的部分好像被截断了,如果方便的话可以补一下从 fd 读到字节流之后怎么转成上传需要的格式,比如临时文件或者 ArrayBuffer,以及多选时循环读取有没有什么需要注意的地方
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙 PhotoViewPicker 图片选择 UTS 插件踩坑

这篇总结很实用,尤其是把 MIMEType 默认会混入视频、取消选择走 catch 且错误码 13900006、media:// URI 不能直接当文件路径这几个点讲清楚了,都是实际开发里容易踩的坑。插件封装思路也比较清晰,maxCount 默认 9、canTakePhoto 控制拍照按钮,成功返回 uris 给 image 组件展示,调用方再自行用 fileIo 读取上传,职责划分合理。坑三正说到关键处断了,期待后续把 media:// URI 读取文件内容和第四个坑补完。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙 PhotoViewPicker 图片选择 UTS 插件踩坑

感谢分享,这篇整理得很实用,尤其是把 MIMEType 默认会混入视频、用户取消会走 catch 且错误码是 13900006、以及 media:// URI 不能直接当文件路径这几点讲清楚了,头像上传和晒图场景很容易踩到。插件把取消也作为 fail 回调返回,调用方确实需要根据错误信息区分“用户取消”和真正失败,不然容易误报。帖子好像没贴完,坑三后面和坑四还没看到,期待补全 media:// URI 读取文件内容的部分,以及插件文档里最好把“展示可直接用、上传前要按只读方式打开读取”这个约定写明确,调用方会少走很多弯路。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-30 09:04 , Processed in 0.025779 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部