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

调用相机拍摄转Bitmap时遇文件描述符ENOENT异常求助

解决相机拍照后转换Bitmap时出现的ENOENT文件不存在异常

你碰到的open failed: ENOENT (No such file or directory)错误,核心原因是相机并没有把照片保存到你指定的临时文件路径,再加上Android 7.0+的文件访问权限限制,导致后续读取文件时找不到目标。咱们一步步拆解问题,把代码修复好:

先分析原代码的核心问题

  1. 启动相机时没有明确指定照片保存路径:原代码里只创建了临时文件对象,但没通过MediaStore.EXTRA_OUTPUT告诉相机要把照片存到这里,相机自然不会生成这个文件。
  2. 适配问题:Android 7.0+禁止直接用Uri.fromFile()暴露本地文件路径,相机可能因此没有写入权限,同样会导致文件创建失败。
  3. 空值处理缺失: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:33:38