You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 相册选图后读取图片字节用于上传的实现问题

问题原因

你之前的代码失效核心原因是依赖了已废弃的MediaStore.Images.Media.DATA字段获取文件真实路径:

  • 从Android 10(API 29)分区存储机制生效开始,应用即使拿到了文件的真实路径字符串,也没有权限直接通过File类访问非应用私有目录下的文件,尤其是路径带类似/storage/3234-3064/的外置SD卡目录时,直接读取必然失败
  • Android 13及以上版本中,DATA字段可能直接返回空值,完全无法拿到路径
  • 你的setImageURI能正常显示图片,是因为该方法内部本身就是通过ContentResolver打开输入流加载图片,不需要真实文件路径,和你读文件的逻辑没有关联
正确实现方案

不要尝试获取文件真实路径,直接通过ContentResolver打开返回Uri对应的输入流读取字节即可,这是官方推荐的全Android版本兼容方案,拿到相册返回的Uri时系统已经给了你临时读取权限,不需要额外做权限申请。
你已经引入了commons-io依赖,可以直接用它的流读取方法,代码如下:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == Constants.INTENT_ID_GALLERY && resultCode == RESULT_OK) {
        val imageUri = data?.data ?: return
        var imageBytes: ByteArray? = null
        var fileName = "upload_image.jpg"
        var mimeType = "image/jpeg"

        try {
            // 1. 读取图片字节数据
            imageBytes = contentResolver.openInputStream(imageUri)?.use { inputStream ->
                org.apache.commons.io.IOUtils.toByteArray(inputStream)
            }

            // 2. 可选:读取上传需要的文件名、文件类型
            contentResolver.query(imageUri, null, null, null, null)?.use { cursor ->
                if (cursor.moveToFirst()) {
                    val nameCol = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
                    val mimeCol = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
                    if (nameCol != -1) cursor.getString(nameCol)?.let { fileName = it }
                    if (mimeCol != -1) cursor.getString(mimeCol)?.let { mimeType = it }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            // 这里添加读取失败的提示逻辑即可
        }

        // 拿到imageBytes、fileName、mimeType后就可以直接走上传逻辑
        if (imageBytes != null) {
            // 执行上传操作
        }
    }
}
注意事项
  • 所有通过ACTION_PICK、ACTION_GET_CONTENT、系统文件选择器返回的Uri,都不要尝试转真实路径,统一用ContentResolver打开流的方式读取,适配所有场景(包括SD卡文件、其他应用共享的文件、云盘同步的文件等)
  • 上面代码中用到的use是Kotlin的内置扩展函数,会在代码块执行完成后自动关闭输入流、Cursor,不需要手动调用close方法,避免内存泄漏
  • 读取流的操作属于IO操作,不要在主线程执行,实际项目中请放到协程IO线程或者工作线程中处理,避免阻塞主线程触发ANR

内容的提问来源于stack exchange,提问作者mario83

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:09:34