更新记录

1.0.0(2026-04-08)

  • 首发版本
  • 支持 Android 13+ Photo Picker
  • 支持低版本系统文档选择器回退
  • 减少广泛媒体权限申请风险
  • 提供 Android 上传路径兼容处理
  • 提供单图、多图与选图即上传能力
  • 解决google play新政策禁止添加媒体权限的问题

平台兼容性

uni-app(3.7.3)

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

Android Photo Picker 免媒体权限相册选择器

面向 uni-app 生产项目的系统相册选择增强插件。

3 行看懂

  • Android 13+ 优先系统 Photo Picker
  • 尽量减少 READ_MEDIA_IMAGES / READ_EXTERNAL_STORAGE 这类广泛媒体权限申请风险
  • 返回 tempFilePaths,尽量不改你现有页面和上传逻辑

只需要调这一个方法

如果你当前只想完成“从系统相册选择图片”,直接调用:

  • chooseImageCompat
import { chooseImageCompat } from '@/uni_modules/simple-photopicker/js_sdk/index.js'

async function chooseImages() {
  try {
    const res = await chooseImageCompat({
      count: 9,
      sourceType: ['album'],
      sizeType: ['compressed']
    })
    return res.tempFilePaths || []
  } catch (error) {
    const errCode = Number(error && error.errCode)
    if (errCode === 1101001) {
      // 用户取消选择时静默返回
      return []
    }
    throw error
  }
}

适合什么项目

  • 想把 Android 图片选择升级为系统 Photo Picker
  • 想减少 Google Play 媒体权限相关审核风险
  • 想解决 content://、外部缓存路径导致 uni.uploadFile 失败的问题
  • 想做发帖、反馈、头像、评价、售后、认证等统一选图方案
  • 想保留 uni.chooseImage 的调用习惯,但补齐 Android 稳定性

核心能力

  • Android 13+ Photo Picker
  • Android 低版本 ACTION_OPEN_DOCUMENT
  • uni.chooseImage 多端回退
  • 纯相册场景自动启用系统选择器
  • Android 上传路径兼容处理
  • 单张上传封装
  • 多张并发上传封装
  • 一步式“选图并上传”
  • 自定义上传请求参数
  • 自定义上传响应解析

适用场景

  • 社区发帖上传
  • 商品评价晒图
  • 售后凭证上传
  • 头像上传
  • 身份认证资料上传
  • 反馈截图上传
  • 客服留言图片上传

兼容性

平台 支持情况 说明
App Android 支持 Android 13+ 优先 Photo Picker
App iOS 支持 回退 uni.chooseImage
H5 支持 回退 uni.chooseImage
微信小程序 支持 回退 uni.chooseImage
uni-app x Android 支持原生模块 以 UTS 能力为主

目录结构

simple-photopicker
├─ package.json
├─ readme.md
├─ changelog.md
├─ market-copy.md
├─ demo
│  └─ demo.vue
├─ js_sdk
│  └─ index.js
└─ utssdk
   ├─ interface.uts
   ├─ unierror.uts
   └─ app-android
      ├─ config.json
      └─ index.uts

快速开始

如果你还需要上传,再按需导入下面这些方法:

import {
  chooseImageCompat,
  uploadImageByBatch,
  uploadImagesByBatch,
  chooseAndUploadImages
} from '@/uni_modules/simple-photopicker/js_sdk/index.js'

最小接入代码

页面里最常见的接法就是这样:

import { chooseImageCompat } from '@/uni_modules/simple-photopicker/js_sdk/index.js'

export default {
  methods: {
    async onChooseImage() {
      try {
        const res = await chooseImageCompat({
          count: 9,
          sourceType: ['album'],
          sizeType: ['compressed']
        })
        console.log('tempFilePaths', res.tempFilePaths)
      } catch (error) {
        const errCode = Number(error && error.errCode)
        if (errCode === 1101001) {
          return
        }
        uni.showToast({
          title: '选择图片失败',
          icon: 'none'
        })
      }
    }
  }
}

进阶用法

单张上传示例

uploadImageByBatch(filePath, {
  uploadUrl: 'https://your-domain.com/common/uploadBatch',
  header: {
    token: 'your-token'
  },
  name: 'file[]'
}).then((result) => {
  console.log(result.urls)
})

批量上传示例

uploadImagesByBatch(tempFilePaths, {
  uploadUrl: 'https://your-domain.com/common/uploadBatch',
  header: {
    token: 'your-token'
  },
  name: 'file[]',
  concurrency: 2,
  onItemSuccess({ urls, index }) {
    console.log('success', index, urls)
  },
  onItemFail({ error, index }) {
    console.log('fail', index, error)
  }
}).then(({ urls, failures }) => {
  console.log(urls, failures)
})

一步式选图并上传

chooseAndUploadImages({
  count: 9,
  sourceType: ['album'],
  sizeType: ['compressed'],
  uploadUrl: 'https://your-domain.com/common/uploadBatch',
  header: {
    token: 'your-token'
  },
  name: 'file[]',
  responseParser(responseData) {
    const data = typeof responseData === 'string' ? JSON.parse(responseData) : responseData
    const body = data.res || data
    const list = body.data && Array.isArray(body.data.list) ? body.data.list : []
    return {
      ok: body.code === 1 || body.code === '1',
      urls: list.map(item => item.url).filter(Boolean),
      message: body.msg || '上传失败'
    }
  }
}).then((result) => {
  console.log('selected', result.chooseResult)
  console.log('uploaded urls', result.urls)
})

API 说明

chooseImageCompat(options)

增强版选图方法。

特点:

  • Android App 纯相册场景优先系统 Photo Picker
  • 非 Android 或不满足条件时自动回退 uni.chooseImage
  • 支持回调写法和 Promise

常用参数:

  • count
  • sourceType
  • sizeType
  • success
  • fail
  • complete

normalizeAndroidUploadPath(path)

把 Android 下不稳定的临时路径转为更适合 uni.uploadFile 的路径。

normalizeAndroidUploadPaths(paths)

批量处理 Android 图片路径。

uploadImageByBatch(filePath, options)

上传单张图片。

常用参数:

  • uploadUrl
  • header
  • formData
  • name
  • fallbackMessage
  • responseParser

uploadImagesByBatch(filePaths, options)

上传多张图片。

额外参数:

  • concurrency
  • onItemSuccess
  • onItemFail
  • onItemComplete

chooseAndUploadImages(options)

先选图,再执行上传。

适合需要快速接入的页面场景。

默认响应解析

如果你不传 responseParser,插件会尽量兼容以下常见返回结构:

  • data.res || data
  • body.data.list
  • body.data.files
  • body.data.url
  • body.data.fullurl

只要最终能解析出可用 urls,就会视为成功。

Demo

插件内附了演示页:

demo/demo.vue

你可以直接复制到自己的测试项目中运行,快速验证:

  • 只选图
  • 选图并上传
  • 上传字段名配置
  • Token Header 配置
  • 返回结果展示

常见问题

1. 为什么 Android 只推荐 sourceType: ['album']

因为这个插件主打的是系统相册优先方案。纯相册场景下,Android 13+ 能优先使用 Photo Picker,更稳也更利于权限收敛。

2. 为什么有些接口返回相对路径?

这是业务后端自己的设计。插件只负责把返回结果解析出来。若你的后端返回相对路径,建议在 responseParser 里自行拼接 CDN / OSS 域名。

3. 这个插件能直接替代项目里所有上传页吗?

可以。论坛、反馈、头像、评价、认证、售后这类页面都适合统一改造。

4. 插件是否强依赖某个后端?

不依赖。上传地址、Header、表单字段名、返回解析器都可自定义。

发布建议

这份插件包已经整理成可上架版本,发布前你只需要再确认这几项:

  • 你的 DCloud 作者 ID 是否为 stayhero,如果不是,请把 package.json 里的 id 改成 你的作者ID-插件英文名
  • 在插件市场后台上传 3 到 5 张主图/功能图
  • 如需调整售价,可直接修改 package.json 里的 regular / sourcecode 价格

商用说明

这套插件更适合面向以下用户:

  • 做论坛/社区 App 的开发者
  • 做商城/评价/售后系统的开发者
  • 正在做 Google Play / Android 权限收敛优化的开发者
  • 想把零散上传逻辑抽成基础设施的团队

更新记录

详见:

changelog.md

隐私、权限声明

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

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

本插件自身不采集、不存储、不上传任何用户数据,不内置第三方统计、分析、广告或远程服务地址。插件仅在用户主动选择图片时,调用系统图片选择能力,并将本地临时文件路径(tempFilePaths)返回给宿主应用使用。本插件不内置固定上传接口、上传服务器地址或数据上报逻辑。若宿主应用自行调用上传接口,相关数据传输行为、服务器地址及用途均由宿主应用决定,与本插件无关。

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

暂无用户评论。