更新记录
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.getFileInfo、uni.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.chooseMedia 的 mediaType 实际上也没法在一次会话里让用户既挑照片又挑视频——用户要传「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.xml、utssdk/app-*/config.json、宿主权限、Gradle/CocoaPods/Framework 或原生资源必须重打基座。
特别注意:utssdk/app-android/config.json 和 utssdk/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.HEIC → IMG_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:为什么图片的 thumbTempFilePath 和 tempFilePath 一样?
图片本身就能直接渲染,再生成一份缩略图纯属浪费。回填自身路径是为了让前端统一只认 thumbTempFilePath,不用对图片和视频写两套渲染分支。
Q:为什么 GIF 选完还是原来那么大?
GIF 和 WebP 可能是动图,重编码只会留下第一帧、动画就没了。所以插件对这两类只做原样拷贝,sizeType: ['compressed'] 对它们不生效。只有 JPEG / PNG / HEIC 会被重编码,其中 PNG 用 PNG 编码保住透明通道。
Q:maxFileSize 是选之前限制还是选之后?
system 模式下 UI 是系统的,插件无法在用户勾选的瞬间拦截,只能选完再校验并返回 9012008;custom 模式则在选中时即时校验时长、超限当场拦截。另外,会被压缩的图片按压缩后的体积校验——原图 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 或包名需重新购买。

收藏人数:
https://gitee.com/harveyzoo/dcloud-uts-zoo-media-picker
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 18
赞赏 0
下载 12559290
赞赏 1948
赞赏
京公网安备:11010802035340号