解决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 转化的地方,勾选原图也可以实现图片的正常上传
  • 于是又对文章阅读了两遍,发现对 系统公共目录的描述中有这样一句话在这里插入图片描述
  • 所以猜测 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 可以将选择自相册(系统公共目录)的文件,另存到应用沙盒目录中,由此便可以规避各种各样的操作问题了。

  • 26
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值