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

安卓从相册选图解码失败,遇FileNotFoundException,请求代码修复

解决相册图片解码失败的FileNotFoundException问题

看起来你遇到的核心问题是错误地将相册返回的Content Uri转换为本地文件路径,导致BitmapFactory找不到对应的文件。Android的相册应用返回的data.getData()是content://开头的Uri,它指向的文件不一定在你假设的Environment.DIRECTORY_PICTURES目录里,尤其是在Android 10及以上的Scoped Storage机制下,直接通过路径访问文件会被限制。

问题出在哪几行?

  • 你通过data.data?.lastPathSegment!!获取的路径片段不能直接用来拼接公共目录路径,这不是文件的真实路径。
  • 你构造了一个不存在的File对象,然后用这个错误的File生成Uri去解码Bitmap,自然会报找不到文件的错误。

修复方案

我们需要通过ContentResolver来读取Content Uri对应的文件流,而不是直接访问文件路径。下面是修正后的完整代码:

class TambahDataActivity : AppCompatActivity() {
    private val GALLERY_REQUEST_CODE = 101
    private var encoded_string: String? = null
    private var image_name: String? = null
    private val STORAGE_PERMISSION_CODE = 1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tambah_data)
        btn_tambah_foto.setOnClickListener {
            if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
                val intent = Intent(Intent.ACTION_PICK)
                intent.type = "image/*"
                // 移除多余的EXTRA_OUTPUT,ACTION_PICK不需要这个参数
                startActivityForResult(intent, GALLERY_REQUEST_CODE)
            } else {
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK && data != null) {
            when (requestCode) {
                GALLERY_REQUEST_CODE -> {
                    val contentUri = data.data ?: return
                    // 显示图片到ImageView
                    img_adu.setImageURI(contentUri)
                    img_adu.visibility = View.VISIBLE

                    // 获取图片文件名(可选,用于后续命名)
                    image_name = getFileNameFromContentUri(contentUri)

                    // 通过ContentResolver获取文件输入流,解码Bitmap并转为Base64
                    contentResolver.openInputStream(contentUri)?.use { inputStream ->
                        val bitmap = BitmapFactory.decodeStream(inputStream)
                        bitmap?.let {
                            val stream = ByteArrayOutputStream()
                            it.compress(Bitmap.CompressFormat.JPEG, 100, stream)
                            val array = stream.toByteArray()
                            encoded_string = Base64.encodeToString(array, Base64.DEFAULT)
                            it.recycle()
                        }
                    }
                }
            }
        }
    }

    // 辅助方法:从Content Uri获取文件名
    private fun getFileNameFromContentUri(uri: Uri): String? {
        var fileName: String? = null
        val cursor = contentResolver.query(uri, null, null, null, null)
        cursor?.use {
            if (it.moveToFirst()) {
                val nameIndex = it.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
                if (nameIndex != -1) {
                    fileName = it.getString(nameIndex)
                }
            }
        }
        return fileName
    }
}

关键改进点

  1. 移除了错误的文件路径构造逻辑:不再通过lastPathSegment拼接公共目录路径,而是直接使用Content Uri通过ContentResolver访问文件。
  2. 使用openInputStream读取文件:这是Android访问Content Uri资源的标准方式,兼容所有Android版本,包括Scoped Storage。
  3. 添加了安全的文件名获取方法:通过查询MediaStore获取真实的文件名,而不是自己推断。
  4. 移除了多余的EXTRA_OUTPUT参数:ACTION_PICK意图不需要这个参数,它是用于拍照保存图片的。

另外,如果你是在Android 10及以上版本开发,建议考虑使用Scoped Storage的API,避免申请READ_EXTERNAL_STORAGE权限(通过ACTION_OPEN_DOCUMENT意图可以获取持久的访问权限),这样更符合现代Android的权限规范。

内容的提问来源于stack exchange,提问作者Dwitio Ahmad Pranoto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 00:19:08