更新记录

0.2.0(2026-07-20)

  • 新增 custom 自绘相册(序号选择、即时校验、大图预览、主题色、中英文),Android 无权限时自动降级 system
  • 新增 camera 系统相机拍照/录像
  • system 模式:Android Photo Picker / iOS PHPicker,零权限;自动 HEIC 转 JPG、视频抽帧封面

平台兼容性

uni-app(3.99)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - 9.0 12 -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

其他

多语言 暗黑模式 宽屏模式
× ×

zoo-media-picker

一次打开相册,图片和视频同屏混排、混着一次选完

uni.chooseImage 只能选图,uni.chooseMediamediaType 实际上也没法在一次会话里让用户既挑照片又挑视频——用户要传「3 张照片 + 1 段视频」,就得点两次入口、走两遍流程。这个插件解决的就是这件事。

特性

  • 图片视频混选:一次打开,同屏混排,任意勾选
  • 零权限:Android 走系统 Photo Picker、iOS 走 PHPicker,不申请 READ_MEDIA_IMAGES,不触发相册授权弹窗,Google Play 敏感权限声明表也不用填
  • 零第三方依赖:不引 Glide / SDWebImage / RxJava,不和宿主 App 已有的库打架
  • 脏活都包了:HEIC/HEIF 自动转 JPG、视频自动抽首帧封面、iCloud 未下载资源自动拉取、Android content:// 自动落盘成可上传的真实文件
  • API 对齐官方:走 uni.chooseMedia 的参数与返回约定,另附 Promise 版本

快速开始

import { chooseMediaPro } from '@/uni_modules/zoo-media-picker'

chooseMediaPro({
  count: 9,
  mediaType: ['image', 'video'],
  success: (res) => {
    res.tempFiles.forEach((file) => {
      console.log(file.mediaType, file.tempFilePath, file.size)
    })
  },
  fail: (err) => {
    console.log(err.errCode, err.errMsg)
  }
})

Promise 版本:

import { chooseMediaProAsync } from '@/uni_modules/zoo-media-picker'

try {
  const res = await chooseMediaProAsync({ count: 9 })
  console.log(res.tempFiles)
} catch (err) {
  console.log(err.errCode, err.errMsg)
}

仅支持 App(Android / iOS),H5 与小程序不支持。 UTS 插件必须打自定义基座才能真机调试,标准基座下不生效。

参数

参数 类型 默认值 说明
count number 9 最多可选/上传数量。不传默认 9;可自定义,取值会夹紧到 1 ~ 99
mediaType string[] ['image', 'video'] 混选就两个都传。传空数组或不传等同于混选
openType string 'system' 'system'=系统选择器(零权限);'custom'=自绘相册 UI(需宿主声明媒体权限,见下);'camera'=系统相机拍照/录像
sizeType string[] ['compressed'] 'compressed' 时压缩图片(长边 1280、质量 80);仅传 ['original'] 时保留原图
maxDuration number 0 视频最长秒数,0 表示不限制
maxFileSize number 0 单文件字节上限,0 表示不限制
convertHeic boolean true HEIC/HEIF 是否转成 JPG
extractVideoThumb boolean true 是否抽视频首帧作为封面
theme object { primaryColor },作用于 custom 模式的选中圈/序号/完成按钮
language string custom 模式 UI 语言:'en' 英文,空或其它跟随系统

返回

success 回调收到:

{
  tempFiles: ZooMediaFileItem[]
  openTypeUsed: string   // 实际生效的模式,请求 custom 时这里会是 'system'
}

每个 ZooMediaFileItem

字段 类型 说明
tempFilePath string 沙盒内临时文件绝对路径,可直接喂 uni.uploadFile
thumbTempFilePath string 视频封面;图片时回填图片自身路径,方便前端统一只认这一个字段
fileName string 保留可读原名,后缀已归一化(IMG_001.HEICIMG_001.jpg
fileExtension string 已归一化的后缀,HEIC 转码后就是 jpg
size number 字节。取的是落盘后的真实体积,压缩过的图片就是压缩后的大小
mediaType string 'image' | 'video'
width number 像素。竖屏视频已按旋转元数据换算成显示尺寸
height number 像素
duration number 视频毫秒;图片恒为 0

错误码

errCode 含义
9012001 当前页面无法打开媒体选择器
9012002 当前已有媒体选择任务正在执行
9012003 用户取消了媒体选择
9012004 未获取到有效的媒体文件
9012005 相册权限被拒绝
9012006 选择数量超出限制
9012007 视频时长超出限制
9012008 文件体积超出限制
9012009 复制文件到应用目录失败
9012010 iCloud 资源下载失败
9012011 当前平台暂不支持媒体选择
9012012 媒体选择器初始化失败
9012013 媒体文件处理失败

errMsg 会带上具体是哪个文件、超了多少,可直接提示给用户。

三种模式(openType)

模式 说明 权限前提
system(默认) 系统选择器,图片视频混选 零权限
custom 自绘相册 UI:选中序号角标、时长/体积即时校验、大图预览、主题色、中英文 需宿主声明媒体权限;未声明时 Android 自动降级 system,iOS 运行时被拒也降级
camera 系统相机拍照/录像 调用方无需 Android CAMERA 权限(Intent 委托);iOS 需宿主声明 NSCameraUsageDescription(录像另需 NSMicrophoneUsageDescription

任何降级都通过 success.openTypeUsed 如实告知实际生效的模式,不走 fail。请求 custom 但被降级时 openTypeUsed 会是 'system'

custom 模式所需权限(宿主自行在 manifest.json 声明):

  • Android:READ_MEDIA_IMAGES / READ_MEDIA_VIDEO(13+),READ_EXTERNAL_STORAGE(≤12)。上架 Google Play 的宿主需自行处理敏感权限声明;不上架商店的宿主无此顾虑。
  • iOS:NSPhotoLibraryUsageDescription

各平台行为

Android

系统版本 实际走的选择器 权限
13+ 系统 Photo Picker(ACTION_PICK_IMAGES 无需权限
11–12 Photo Picker backport(随 Google Play 系统更新下发) 无需权限
≤10,或无 Play 服务的设备 ACTION_GET_CONTENT + 多选 无需权限
  • 插件不在 AndroidManifest 里声明任何权限,装上不会给宿主 App 增加任何权限。
  • count 在 Photo Picker 上由系统强制;ACTION_GET_CONTENT 分支系统不限数量,插件会在选完后校验并返回 9012006

iOS

系统版本 实际走的选择器 权限
14+ PHPickerViewController 无需相册权限
≤13 UIImagePickerController单选 需宿主声明 NSPhotoLibraryUsageDescription
  • iOS 14+ 是绝对主流路径,不需要任何隐私描述。
  • iOS 13 及以下:插件坚持不硬写隐私描述(否则会给宿主 App 平白加上相册权限、影响审核)。宿主若没声明,插件返回 9012005 而不是闪退。要支持这条分支,请在宿主的 manifest.json → App 权限配置里补 NSPhotoLibraryUsageDescription

FAQ

Q:为什么图片的 thumbTempFilePathtempFilePath 一样? 图片本身就能直接渲染,再生成一份缩略图纯属浪费。回填自身路径是为了让前端统一只认 thumbTempFilePath,不用对图片和视频写两套渲染分支。

Q:为什么 GIF 选完还是原来那么大? GIF 和 WebP 可能是动图,重编码只会留下第一帧、动画就没了。所以插件对这两类只做原样拷贝,sizeType: ['compressed'] 对它们不生效。只有 JPEG / PNG / HEIC 会被重编码,其中 PNG 用 PNG 编码保住透明通道。

Q:maxFileSize 是选之前限制还是选之后? system 模式下 UI 是系统的,插件无法在用户勾选的瞬间拦截,只能选完再校验并返回 9012008custom 模式则在选中时即时校验时长、超限当场拦截。另外,会被压缩的图片按压缩后的体积校验——原图 12MB、压缩后 800KB,在 maxFileSize: 1MB 下是通过的。

Q:视频会被压缩吗? 不会。sizeType 只作用于图片。视频原样落盘,避免转码耗时和画质损失。

Q:HEIC 一定能转成 JPG 吗? Android 9 以下的系统解码器不支持 HEIF,这种情况下插件会原样拷贝,并如实fileExtension 保留为 heic(不会谎报成 jpg),前端可据此自行降级。iOS 全版本原生支持,不受影响。

Q:选中的顺序能保证吗? 能。iOS 15+ 开了 .ordered 选择模式,返回顺序即用户勾选顺序;Android Photo Picker 与系统返回顺序一致。

授权说明

本插件按 uni appid + 包名 粒度授权,一次购买绑定一个应用。更换 appid 或包名需重新购买。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

system 模式(默认)无需任何权限。custom 模式需宿主 App 自行在 manifest 声明 Android 媒体权限(READ_MEDIA_IMAGES / READ_MEDIA_VIDEO;Android 12 及以下用 READ_EXTERNAL_STORAGE)、iOS 声明 NSPhotoLibraryUsageDescription。camera 模式需相机权限(iOS 声明 NSCameraUsageDescription,录像另需 NSMicrophoneUsageDescription)。插件本体不声明任何权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集、不上传任何数据,所选或拍摄的媒体仅写入应用自身的临时目录。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。