Android开发:如何从ActivityResultLauncher的Uri获取真实文件路径
问题描述
我正在开发一项拍照上传至服务器的功能,需以multipart/form-data格式向服务器发送数据,因此需要从ActivityResultLauncher获取的Uri创建文件。但该Uri为伪路径,由此创建的文件并不存在。请问如何从ActivityResultLauncher获取的Uri中获取真实路径?
更新:以下是我的代码
AndroidManifest.xml 配置
<provider android:authorities="com.tgddssss.projectcf.fileProvider" android:name="androidx.core.content.FileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>
@xml/file_paths 配置
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="camera_photos" path="."/> </paths>
创建Uri的函数
fun createUri(): Uri { val currentTime = System.currentTimeMillis() val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()) val currentTimeString = dateFormat.format(Date(currentTime)) val fileName = "photo_$currentTimeString.jpg" val file = File(requireActivity()?.applicationContext?.filesDir, fileName) return FileProvider.getUriForFile( requireActivity().applicationContext, "com.tgddssss.projectcf.fileProvider", file ) }
注册拍照Launcher的函数
fun registerPictureLauncher(imageUri: Uri?): ActivityResultLauncher<Uri> { return registerForActivityResult( ActivityResultContracts.TakePicture(), ActivityResultCallback { try { if (it) { val file = File(imageUri.toString()) Log.d("TAG","${file.exists()}") } } catch (e: Exception) { e.printStackTrace() } } ) }
另外使用Permission Dispatcher进行权限检查...
解决方案
问题根源
你在registerPictureLauncher里直接用File(imageUri.toString())创建文件是错误的——imageUri是FileProvider生成的content://格式Uri,并非本地文件路径,所以这样生成的File必然不存在。
最优解决方式:复用已创建的File对象
其实你不需要从Uri反向获取真实路径,因为在createUri()方法里你已经明确创建了对应的本地File对象,只需要把这个File对象保存下来,在拍照成功后直接使用即可:
修改
createUri(),返回File和Uri的组合
把原来只返回Uri改成返回包含File和Uri的封装类,或者直接将File保存为成员变量:// 定义数据类封装文件信息 data class CameraFileInfo(val file: File, val uri: Uri) fun createCameraFileInfo(): CameraFileInfo { val currentTime = System.currentTimeMillis() val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()) val currentTimeString = dateFormat.format(Date(currentTime)) val fileName = "photo_$currentTimeString.jpg" val file = File(requireActivity().applicationContext.filesDir, fileName) val uri = FileProvider.getUriForFile( requireActivity().applicationContext, "com.tgddssss.projectcf.fileProvider", file ) return CameraFileInfo(file, uri) }注册Launcher时传入并复用File对象
调用createCameraFileInfo()获取文件和Uri,在回调中直接使用已保存的File:// 获取拍照用的文件信息 val cameraFileInfo = createCameraFileInfo() // 注册拍照Launcher val pictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success -> if (success) { // 直接使用预先创建好的File对象 val targetFile = cameraFileInfo.file Log.d("TAG", "文件是否存在:${targetFile.exists()}") // 此处可直接用targetFile构建multipart/form-data请求上传 } } // 启动拍照 pictureLauncher.launch(cameraFileInfo.uri)
备用方案:从任意Uri生成可访问File
如果后续需要处理非自行生成的Uri(比如相册选择的图片),可以通过ContentResolver读取Uri对应的文件流,复制到应用私有目录生成可访问的File:
fun uriToFile(context: Context, uri: Uri): File? { return try { val inputStream = context.contentResolver.openInputStream(uri) ?: return null val tempFile = File(context.filesDir, "temp_${System.currentTimeMillis()}.jpg") FileOutputStream(tempFile).use { outputStream -> inputStream.copyTo(outputStream) } tempFile } catch (e: Exception) { e.printStackTrace() null } }
注意事项
- 拍照成功后,
ActivityResultContracts.TakePicture()返回true即说明照片已写入你指定的File中,此时cameraFileInfo.file必然存在。 - 上传完成后,可根据需求删除临时文件,避免占用存储空间。
内容的提问来源于stack exchange,提问作者Hùng Nguyễn Quý
相关产品推荐
相关产品推荐

