首页 > 建站教程 > JS、jQ、TS >  解决uni.chooseImage勾选相册原图,使用pathToBase64方法转Base64,提示“targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径”的问题正文

解决uni.chooseImage勾选相册原图,使用pathToBase64方法转Base64,提示“targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径”的问题

解决uni.chooseImage勾选相册原图,使用pathToBase64方法转Base64,提示“targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径”的问题:


1.问题描述

上传图片时,后端接收的参数类型为 Base64,于是我们便使用 uniapp 中的 uni.chooseImage API,搭配 image-tools 插件的 pathToBase64 方法来实现传递 Base64 格式图片的业务需求。


// 业务代码
import { pathToBase64 } from '@/utils/image-tools.js'
uni.chooseImage({
  count: 1,
  sourceType: ['camera', 'album'],
  async success(res) {
    let ewm = res.tempFilePaths[0]
    ewm = await pathToBase64(ewm)
const data = {
  formData: {
    ewm
  }
}
    const {data: res} = await _this.networkApi(data)
    // 下方省略
  },
})


H5 端没有问题,但在安卓APP 中,当用户勾选原图之后,便出现了问题,经调试排查,发现 await pathToBase64(ewm) 抛出了一个异常


{
    "type": "error",
    "bubbles": false,
    "cancelBubble": false,
    "cancelable": false,
    "lengthComputable": false,
    "loaded": 0,
    "total": 0,
    "target": {
        "fileName": "/storage/emulated/0/Pictures/Gallery/owner/xxx/IMG_20240202_133858.jpg",
        "readyState": 2,
        "result": null,
        "error": {
            "code": 15,
            "message": "targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!具体请看:https://ask.dcloud.net.cn/article/36199"
        },
        "onloadstart": null,
        "onprogress": null,
        "onload": "function() { [native code] }",
        "onabort": null,
        "onerror": "function() { [native code] }",
        "onloadend": null
    }
}


2.解决方案

经过阅读文章 https://ask.dcloud.net.cn/article/id-36199,并经过有关尝试后,有以下三种解决方案


2.1 后端不使用 Base64 的格式接收图片

2.2 在 uni.chooseImage API中添加 sizeType: [‘compressed’] 配置,仅允许选择压缩图


// 业务代码
import { pathToBase64 } from '@/utils/image-tools.js'
uni.chooseImage({
  count: 1,
  sourceType: ['camera', 'album'],
  // 添加该配置,仅使用 压缩图
  sizeType: ['compressed'],
  async success(res) {
    let ewm = res.tempFilePaths[0]
    ewm = await pathToBase64(ewm)
const data = {
  formData: {
    ewm
  }
}
    const {data: res} = await _this.networkApi(data)
    // 下方省略
  },
})


2.3 使用 uni.saveFile 方法另存图片后,再转 Base64

utils 下封装如下方法


export const saveFileSync = (tempFilePath) => {
   return new Promise((resolve, reject) => {
     uni.saveFile({
       tempFilePath,
       success: function (file) {
         resolve(file.savedFilePath)
       },
       fail: function (error) {
         reject(error)
       }
     })
   })
 }


使用 saveFileSync,将选择的图片另行保存到本地,并获取保存的地址


// 业务代码
import { pathToBase64 } from '@/utils/image-tools.js'
import { saveFileSync } from '@/utils/file.js'
uni.chooseImage({
  count: 1,
  sourceType: ['camera', 'album'],
  async success(res) {
    let ewm = res.tempFilePaths[0]
    // 使用 saveFileSync,将选择的图片另行保存到本地,并获取保存的地址
    const path = await saveFileSync(ewm)
    ewm = await pathToBase64(path)
const data = {
  formData: {
    ewm
  }
}
    const {data: res} = await _this.networkApi(data)
    // 下方省略
  },
})


3.原因探讨

声明:下方大多是我排查BUG、探索问题产生原因的过程记录,开发任务重的同学可以先去忙哈~


用户通过相册选择图片的地址的绝对路径为 file:///storage/emulated/0/Pictures 开头,经过阅读文章 https://ask.dcloud.net.cn/article/id-36199 可以知道,该目录属于系统公共目录

文章中提到:“andorid 11强制执行分区存储。不允许应用读写操作非应用沙盒目录和系统公共目录下的资源文件。dcloud已对分区存储机制做了适配工作。但也增加了开发者对文件目录操作的规则。在分区存储的环境下分出两个可操文件数据目录 系统公共目录 和 应用沙盒目录。”

大概意思就是 安卓自身不允许应用访问自身存储空间(应用沙盒目录)之外的内容,dcloud 对分区存储机制进行了优化,除了自身存储空间外,系统公共目录也能通过一定手段来进行读写

对于系统公共目录,“当我们拥有权限,也能通过路径直接访问。”,这一点,大概就是系统在读写相册前,需要授权的原因,在完成授权后,我们便能够读写 相册 等系统公共目录

由此我猜测,当用户勾选了原图之后, dcloud 对分区存储机制的优化失效了,应用无法访问系统公共目录,导致抛出异常,但经过尝试发现,其他未进行 Base64 转化的地方,勾选原图也可以实现图片的正常上传

于是又对文章阅读了两遍,发现对 系统公共目录的描述中有这样一句话


uni-app


所以猜测 image-tools 中的 pathToBase64 方法,存在对系统公共目录的文件进行了写操作,但经过阅读源码、查阅有关文档发现,应该不存在写操作,也就不会出现对系统公共目录操作越权的行为


export function pathToBase64(path) {
// 省略其他代码
if (typeof plus === 'object') {
  plus.io.resolveLocalFileSystemURL(
    getLocalFilePath(path),
    function(entry) {
      entry.file(
        function(file) {
          var fileReader = new plus.io.FileReader()
          fileReader.onload = function(data) {
            resolve(data.target.result)
          }
          fileReader.onerror = function(error) {
            reject(error)
          }
          fileReader.readAsDataURL(file)
        },
        function(error) {
          reject(error)
        }
      )
    },
    function(error) {
      reject(error)
    }
  )
  return
}
// 省略其他代码
}


至此,我对“解决 uni.chooseImage 勾选相册原图,搭配 image-tools 插件的 pathToBase64 方法转 Base64,提示“targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径”的问题”的探究就无果而终了

有知道原因的同学或前辈可以在评论区留言指教一番,先行感谢~


4.使用 uni.saveFile 方法另存图片,能够解决该问题的原因

接上文,应用程序在分区存储的环境下,分出了两个可操做文件数据目录:系统公共目录 和 应用沙盒目录,尽管 dcloud 对分区存储机制做了适配工作,但系统公共目录的操作权限自然不如应用沙盒目录来的广泛、自由

而使用 uni.saveFile 可以将选择自相册(系统公共目录)的文件,另存到应用沙盒目录中,由此便可以规避各种各样的操作问题了。