Kotlin Fragment中相机Bitmap转URI遇outputFileUri为空的解决问询
我帮你分析下问题所在,然后给出两种针对性的解决思路:
首先,你的outputFileUri为空或无效的核心原因是:
你通过data.getExtras().get("data")获取的是相机返回的内存缩略图Bitmap,它并没有被写入到本地文件中。而你当前的getCaptureImageOutputUri()只是创建了一个空的文件Uri,并没有把这个Bitmap实际写入到对应的文件里,所以这个Uri本质上指向一个不存在的空文件,自然后续路径无效。
另外,这种通过data.getExtras().get("data")拿图片的方式本来就只能得到小尺寸缩略图,不适合上传高清照片,更推荐用提前指定输出文件的相机拍照流程,这也是Android官方推荐的方式。
方案一:官方推荐的相机拍照流程(获取高清原图)
这个方法会让相机直接把拍摄的高清照片写入你指定的文件,这样你能直接拿到有效的文件路径和Uri:
- 首先添加FileProvider配置(Android 7.0+必须,避免
FileUriExposedException)
在AndroidManifest.xml里添加:
<application> ... <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
然后在res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
- 修改启动相机的代码,提前指定输出文件:
private var currentPhotoPath: String? = null private fun dispatchTakePictureIntent() { Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent -> // 确认有相机应用可以处理这个Intent takePictureIntent.resolveActivity(requireContext().packageManager)?.also { val photoFile: File? = try { createImageFile() } catch (ex: IOException) { // 处理文件创建失败的情况 null } photoFile?.also { val photoURI: Uri = FileProvider.getUriForFile( requireContext(), "${requireContext().packageName}.fileprovider", it ) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) } } } } // 创建唯一的图片文件 @Throws(IOException::class) private fun createImageFile(): File { val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", ".jpg", storageDir ).apply { currentPhotoPath = absolutePath // 保存文件路径,后续使用 } }
- 修改
onActivityResult中的相机处理逻辑:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK) { when (requestCode) { REQUEST_IMAGE_CAPTURE -> { val imageView = view!!.findViewById<ImageView>(R.id.display_image) // 直接用之前保存的currentPhotoPath加载图片 currentPhotoPath?.let { path -> mBitmap = BitmapFactory.decodeFile(path) imageView.setImageBitmap(mBitmap) filePath = path // 获取可用于上传的Uri outputFileUri = FileProvider.getUriForFile( requireContext(), "${requireContext().packageName}.fileprovider", File(path) ) intermediate = requireContext().contentResolver.getType(outputFileUri!!) } } IMAGE_PICK_CODE -> { // 你的图片选择逻辑保持不变 val imageView = view!!.findViewById<ImageView>(R.id.display_image) imageUri = data?.data intermediate = activity!!.contentResolver.getType(imageUri!!) filePath = getPathFromURI(imageUri!!) if (filePath != null) { mBitmap = BitmapFactory.decodeFile(filePath) imageView.setImageBitmap(mBitmap) } makeComplaint_button!!.backgroundTintList = activity!!.getColorStateList(R.color.color_selector) makeComplaint_button!!.isEnabled = true } } } }
方案二:如果一定要用data返回的缩略图Bitmap生成文件和Uri
如果你因为某些原因必须用data.getExtras().get("data")的Bitmap,那需要手动把这个Bitmap写入到本地文件,再生成Uri:
// 工具函数:把Bitmap写入文件并返回对应的Uri fun getUriFromBitmap(context: Context, bitmap: Bitmap): Uri? { val filesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) val imageFile = File(filesDir, "captured_thumb_${System.currentTimeMillis()}.png") return try { val outputStream = FileOutputStream(imageFile) bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) outputStream.flush() outputStream.close() // 用FileProvider生成Uri(Android 7.0+必须) FileProvider.getUriForFile( context, "${context.packageName}.fileprovider", imageFile ) } catch (e: IOException) { e.printStackTrace() null } }
然后在onActivityResult中调用:
if (requestCode == REQUEST_IMAGE_CAPTURE){ val imageView = view!!.findViewById<ImageView>(R.id.display_image) photo = data.getExtras().get("data") as Bitmap imageView.setImageBitmap(photo) // 生成Uri和文件路径 outputFileUri = getUriFromBitmap(activity!!, photo!!) outputFileUri?.let { uri -> // 从Uri获取文件路径(可选,根据你的上传逻辑决定) filePath = getPathFromURI(uri) intermediate = activity!!.contentResolver.getType(uri) } }
补充说明
- 方案一得到的是高清原图,适合上传;方案二只有缩略图,清晰度低,不推荐用于上传场景。
- 用
getExternalFilesDir存储文件时,不需要申请运行时存储权限,因为这是应用的私有目录。
内容的提问来源于stack exchange,提问作者Aldor
相关产品推荐
相关产品推荐

