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

Kotlin中相机Intent拍照后如何获取图片URI并保存

解决方案:拍照后获取图片URI并保存到应用目录

你的问题核心是默认的拍照Intent只返回缩略图,无法获取原图URI,要解决这个问题,需要提前指定拍照图片的保存路径,让相机直接将原图保存到你指定的位置,而不是依赖Intent返回的data。以下是完整的修改步骤:


1. 配置FileProvider(Android 7.0+ 必填)

由于Android 7.0及以上禁止直接使用file:// URI访问文件,需要通过FileProvider来授权相机访问应用内部存储的文件:

步骤1:修改AndroidManifest.xml

在<application>标签内添加FileProvider配置:

<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>

步骤2:创建file_paths.xml

在res/xml目录下新建file_paths.xml(如果没有xml目录则创建),配置允许访问的应用内部存储路径:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 授权访问应用内部存储的images目录 -->
    <files-path name="images" path="images/" />
</paths>

2. 修改AddMemorieActivity代码

步骤1:添加全局变量保存拍照URI

在Activity类顶部添加变量:

private var cameraImageUri: Uri? = null

步骤2:重写拍照逻辑(captureImageFromCamera)

修改拍照方法,提前创建保存图片的文件,并将URI传递给相机Intent:

private fun captureImageFromCamera() {
    val perm = checkPermission(Manifest.permission.CAMERA)
    if (perm) {
        try {
            // 创建应用内部存储的images目录
            val directory = File(filesDir, "images")
            if (!directory.exists()) {
                directory.mkdirs()
            }
            // 生成唯一文件名
            val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
            val fileName = "IMG_$timestamp.jpg"
            val imageFile = File(directory, fileName)

            // 通过FileProvider获取授权URI
            cameraImageUri = FileProvider.getUriForFile(
                this,
                "${packageName}.fileprovider", // 对应Manifest里的authorities
                imageFile
            )

            val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            // 告诉相机将图片保存到指定URI
            intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri)
            // 授予相机写入该URI的权限
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)

            if (intent.resolveActivity(packageManager) != null) {
                startActivityForResult(intent, PICK_IMAGE_FROM_CAMERA)
            } else {
                Toast.makeText(this, "相机应用不可用", Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "打开相机时出错", Toast.LENGTH_SHORT).show()
        }
    }
}

步骤3:修改onActivityResult中的相机处理逻辑

拍照完成后,图片已经直接保存到你指定的文件,无需再复制,直接使用路径即可:

else if (requestCode == PICK_IMAGE_FROM_CAMERA && resultCode == RESULT_OK) {
    cameraImageUri?.let { uri ->
        // 从URI获取文件路径
        val imageFile = File(uri.path?.replace("file://", "") ?: "")
        imagePath = imageFile.absolutePath

        val bouton_ajout_image = findViewById<ImageView>(R.id.ajouter_image)
        bouton_ajout_image.setOnClickListener { showImagePickerDialog() }

        Glide.with(this)
            .load(imagePath)
            .into(bouton_ajout_image)
    } ?: run {
        Toast.makeText(this, "无法获取拍照图片", Toast.LENGTH_SHORT).show()
    }
}

步骤4:修复相册选图的URI获取逻辑

你原来的相册选图代码错误地使用了data?.extras?.get("data"),这是相机缩略图的获取方式,相册选图的正确URI是data?.data:

if (requestCode == PICK_IMAGE_FROM_GALLERY && resultCode == RESULT_OK) {
    val imageUri: Uri? = data?.data // 修正此处

    if (imageUri != null) {
        val directory = File(filesDir, "images")
        if (!directory.exists()) {
            directory.mkdirs()
        }

        val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
        val fileName = "IMG_$timestamp.jpg"
        val file = File(directory, fileName)

        try {
            // 用Kotlin扩展函数简化文件复制,自动关闭流
            contentResolver.openInputStream(imageUri)?.use { inputStream ->
                FileOutputStream(file).use { outputStream ->
                    inputStream.copyTo(outputStream)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        imagePath = file.absolutePath

        val bouton_ajout_image = findViewById<ImageView>(R.id.ajouter_image)
        bouton_ajout_image.setOnClickListener { showImagePickerDialog() }

        Glide.with(this)
            .load(imagePath)
            .into(bouton_ajout_image)
    }
}

步骤5:处理权限请求结果

添加权限回调,确保用户授予相机权限后能重新打开相机:

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 0 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        captureImageFromCamera()
    } else {
        Toast.makeText(this, "需要相机权限才能拍照", Toast.LENGTH_SHORT).show()
    }
}

关键说明

  • 为什么不用data?.extras?.get("data"):这个方法返回的是相机压缩后的缩略图,分辨率极低,不适合保存到应用目录。
  • FileProvider的作用:Android 7.0+ 禁止跨应用直接访问file:// URI,FileProvider通过生成content:// URI来授权其他应用(如相机)访问你的应用文件。
  • 自动关闭流:使用Kotlin的use函数可以确保输入输出流在操作完成后自动关闭,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Ameliorer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 23:31:01