调用相机拍摄转Bitmap时遇文件描述符ENOENT异常求助
解决相机拍照后转换Bitmap时出现的ENOENT文件不存在异常
你碰到的open failed: ENOENT (No such file or directory)错误,核心原因是相机并没有把照片保存到你指定的临时文件路径,再加上Android 7.0+的文件访问权限限制,导致后续读取文件时找不到目标。咱们一步步拆解问题,把代码修复好:
先分析原代码的核心问题
- 启动相机时没有明确指定照片保存路径:原代码里只创建了临时文件对象,但没通过
MediaStore.EXTRA_OUTPUT告诉相机要把照片存到这里,相机自然不会生成这个文件。 - 适配问题:Android 7.0+禁止直接用
Uri.fromFile()暴露本地文件路径,相机可能因此没有写入权限,同样会导致文件创建失败。 - 空值处理缺失:
decodeBitmap方法在获取不到fileDescriptor时直接解码,会引发后续崩溃。
修复步骤与代码调整
1. 正确创建临时文件并启动相机
首先实现靠谱的临时文件生成逻辑,并用FileProvider生成安全Uri传给相机(适配Android 7.0+):
// 全局或类内临时变量,保存临时文件路径 private var tempPhotoPath: String = "" private fun getTempFile(context: Context): File { // 用APP私有外部存储,不需要额外申请权限 val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${System.currentTimeMillis()}_", ".jpg", storageDir ).apply { tempPhotoPath = this.absolutePath // 保存路径,后续读取用 } } // 启动相机的代码 fun startCamera(context: Context) { val imageFile = getTempFile(context) val photoUri = FileProvider.getUriForFile( context, "${context.packageName}.fileprovider", // 记得在Manifest注册这个Provider imageFile ) val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) // 告诉相机要把照片存到指定Uri cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri) // 给相机临时写入权限 cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) (context as Activity).startActivityForResult(cameraIntent, REQUEST_CODE_CAMERA) }
2. 修正getImageFromResult的逻辑
因为我们已经指定了相机的输出路径,不需要再从返回的Intent里拿数据,直接用之前保存的临时文件路径即可:
fun getImageFromResult(context: Context, resultCode: Int, imageReturnedIntent: Intent?): Bitmap? { var bm: Bitmap? = null if (resultCode == RESULT_OK) { val selectedImage = Uri.fromFile(File(tempPhotoPath)) selectedPhotoName = selectedImage.toString() bm = getImageResized(context, selectedImage) val rotation = getRotation(context, selectedImage, true) // 确定是相机拍摄的照片 bm = rotate(bm, rotation) } return bm }
3. 完善decodeBitmap的异常处理
避免空值引发的崩溃,同时明确错误信息:
private fun decodeBitmap(context: Context, theUri: Uri, sampleSize: Int): Bitmap { val options = BitmapFactory.Options() options.inSampleSize = sampleSize var fileDescriptor: AssetFileDescriptor? = null try { fileDescriptor = context.contentResolver.openAssetFileDescriptor(theUri, "r") } catch (e: FileNotFoundException) { App.log("CameraPhoto: fileDescriptorErr: ${e.message} (decodeBitmap) ") throw IllegalStateException("目标照片文件不存在,请检查相机权限与存储路径") } catch (e: Exception) { App.log("CameraPhoto: unexpected error: ${e.message} (decodeBitmap) ") throw e } return fileDescriptor?.let { BitmapFactory.decodeFileDescriptor(it.fileDescriptor, null, options) } ?: throw IllegalStateException("无法获取文件描述符") }
4. 注册FileProvider(必须步骤)
在AndroidManifest.xml里添加Provider配置:
<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="camera_photos" path="Pictures" /> </paths>
为什么原代码会出错?
你之前的代码只创建了临时文件对象,但没有告诉相机要把照片存到这个位置——相机默认会把照片存到系统相册,不会主动写入你指定的空文件。再加上Android 7.0+的文件权限限制,直接用Uri.fromFile()会导致相机没有写入权限,双重问题叠加就出现了文件不存在的错误。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

