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
相关产品推荐
相关产品推荐

