更新记录

0.3.3(2026-08-14)

修复 Android custom 自绘相册「左上角第一张选不中」的问题。

  • 修复网格首格(position 0)在页面刚打开时既不响应勾选、也不响应预览的问题:适配器每次复用都新建 AbsListView.LayoutParams 覆盖布局参数,把系统在测量首格时写入的 forceAdd 标记抹掉,导致该 cell 走 attachViewToParent 被塞进列表——能画出来却没真正 attach 到 window,收不到任何触摸;滚动一次后才恢复。现在复用同一个 LayoutParams 对象,仅在缺失或列数变化时更新尺寸。
  • 不影响 iOS:iOS 自绘页基于 UICollectionView,无此复用路径。

0.3.2(2026-08-12)

修复 Android 真机导入宿主后「拿到 tempFilePath 却传不了、预览不了」的问题。

  • Android 选中媒体落盘目录从应用内部 cacheDir/data/user/0/...)改为外部应用私有目录 getExternalFilesDir:uni/plus 的文件 API(uni.getFileInfouni.uploadFile<image>)在 Android 上读不到内部目录,此前宿主上传会报「文件不存在」。无需任何权限,卸载自动清理。
  • 修复 custom 自绘页 Activity 未合入宿主 Manifest 时(基座未带插件配置)硬启动导致原生桥 ClassCastException 崩溃、真实错误被吞成「用户取消」的问题:现在按「降级不走 fail」约定无缝退回 system 选择器,openTypeUsed 如实报 system
  • 修复错误兜底 resolveErrorMessage 对 Android 原生异常强转 UTSError 导致崩桥的问题,改用 instanceof 安全收窄。
  • 修正 runtime-config.uts 的版本号常量与 package.json 不一致的问题(0.3.1 发布时漏同步,导致诊断日志版本不可信)。

0.3.1(2026-07-24)

修复导入实际项目中的,ios端代码编译自定义基座问题

查看更多

平台兼容性

uni-app(4.25)

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

uni-app x(4.25)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

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

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 版本
  • 可诊断、可辨版本:内置运行时配置、逐项能力检测和 buildId,不再只靠 package.json.version 猜实际代码版本

快速开始

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 与小程序不支持。

兼容版本与基座边界

  • 最低 HBuilderX:4.25。插件包含 Kotlin/Swift 原生混编源码,HBuilderX 4.25+ 才支持直接真机联编。
  • Android 最低版本:API 21(Android 5.0);iOS 最低版本:iOS 12。
  • Android system 模式只依赖可联编源码,HBuilderX 4.25+ 且本机 UTS Android 编译环境完整时可使用标准基座开发。
  • Android custom 需要 ZooMediaGridActivity 和宿主媒体权限,camera 需要 ZooFileProvider;这些属于 Manifest/权限配置,首次接入或相关配置变化后必须重新制作自定义基座。
  • iOS 真机运行需要匹配宿主的证书;custom/camera 所需隐私描述进入主 App 的 Info.plist 后才会生效。
  • 已有正确基座后,修改 .uts/.kt/.swift、运行时日志和默认模式通常可继续联编;修改 AndroidManifest.xmlutssdk/app-*/config.json、宿主权限、Gradle/CocoaPods/Framework 或原生资源必须重打基座。

特别注意:utssdk/app-android/config.jsonutssdk/app-ios/config.json 是 DCloud 原生构建配置,不是业务运行时配置。不要把 debug 或模式开关写进去。

运行时配置与诊断

插件根目录提供 zoo-media-picker.config.example.json。它是带说明字段的宿主配置模板;复制到宿主 src/config/ 后,在 App 启动时调用:

import mediaPickerConfig from '@/config/zoo-media-picker.config.json'
import {
  configureMediaPicker,
  getMediaPickerDiagnostics,
  getMediaPickerVersion
} from '@/uni_modules/zoo-media-picker'

configureMediaPicker(mediaPickerConfig)

console.log('zoo-media-picker actual build:', getMediaPickerVersion())
getMediaPickerDiagnostics({ openType: 'custom', print: true })

默认运行时配置:

配置 默认值 说明
debugLogEnabled true 输出流程级 debug 日志
diagnosticsOnChoose true 每次选择前逐项检测并打印;只检测,不新增自动模式切换
errorLogEnabled true 输出关键错误,建议生产环境保持开启
defaultOpenType 'system' 单次调用没传 openType 时才使用;单次参数优先
includeFilePathsInLogs false 是否输出完整沙盒路径

也可以随时直接调用:

configureMediaPicker({
  debugLogEnabled: true,
  diagnosticsOnChoose: true,
  errorLogEnabled: true,
  defaultOpenType: 'custom',
  includeFilePathsInLogs: false
})

能力检测输出 PASS/WARN/FAIL,Android 会检查 Activity、Provider、Manifest 权限和系统 Intent,iOS 会检查隐私描述、授权状态、展示控制器和系统入口。检测结果通过 available 返回,但不会因为新检测项而替调用方切换模式;插件原有的“缺失敏感权限/权限拒绝时安全降级”仍保留,用于避免崩溃。

如何确认实际安装的代码版本

不要只看 package.json.version。应优先调用:

console.log(getMediaPickerVersion())
// 0.2.4 应包含:
// { version: '0.2.4', buildId: '20260724-ios-mutable-array-fix-v1', featureLevel: 'm5.1-settings-ui' }

如果 API 不存在,或者 featureLevel 不是 m5.1-settings-ui,说明项目里仍是旧源码,即使 package.json 被写成了新版本号。

手工检查齿轮入口:

  • Android:utssdk/app-android/ZooMediaGridActivity.kt 中搜索 settingsButton.text = "⚙"
  • iOS:utssdk/app-ios/ZooMediaGridController.swift 中搜索 UIImage(systemName: "gearshape")

参数

参数 类型 默认值 说明
count number 9 最多可选/上传数量。不传默认 9;可自定义,取值会夹紧到 1 ~ 99
mediaType string[] ['image', 'video'] 混选就两个都传。传空数组或不传等同于混选
openType string 运行时 defaultOpenType,初始为 '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

custom 预览支持长图高清查看和双指缩放/拖动。预览里的图片编辑是非破坏式的:画笔、箭头、文字、马赛克、裁剪、旋转都会生成一张插件临时新图,原相册图片不会被修改;最终返回结构仍然是 tempFilePath / thumbTempFilePath 等统一字段。视频暂不支持编辑。

各平台行为

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 模式需宿主声明相册媒体权限;iOS camera 模式需宿主声明相机权限,录像另需麦克风权限

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

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

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

暂无用户评论。