安卓从相册选图解码失败,遇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 } }
关键改进点
- 移除了错误的文件路径构造逻辑:不再通过lastPathSegment拼接公共目录路径,而是直接使用Content Uri通过ContentResolver访问文件。
- 使用
openInputStream读取文件:这是Android访问Content Uri资源的标准方式,兼容所有Android版本,包括Scoped Storage。 - 添加了安全的文件名获取方法:通过查询MediaStore获取真实的文件名,而不是自己推断。
- 移除了多余的
EXTRA_OUTPUT参数:ACTION_PICK意图不需要这个参数,它是用于拍照保存图片的。
另外,如果你是在Android 10及以上版本开发,建议考虑使用Scoped Storage的API,避免申请READ_EXTERNAL_STORAGE权限(通过ACTION_OPEN_DOCUMENT意图可以获取持久的访问权限),这样更符合现代Android的权限规范。
内容的提问来源于stack exchange,提问作者Dwitio Ahmad Pranoto
相关产品推荐
相关产品推荐

