Android Compose中通过URI获取选中媒体文件字节的方法(API34测试)
测试环境:API 34
需求
获取用户选择的照片并发送至服务器
我的实现方案
- 通过Compose的
ActivityResultContracts.PickMultipleVisualMedia调用用户相册选择照片:
val photoPicker = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickMultipleVisualMedia(12) ) { photos -> photos.takeIf { it.isNotEmpty() }?.let { uri -> dispatch(Action.OnClickUploadImages(uri.map { it.toString() })) } }
- 将URI列表保存至
ViewModel,通过Coil展示图片(此步骤正常运行) - 用户点击提交按钮时,将图片发送至服务器:
private fun sendImages() = viewModelScope.launch { useCases.sendImagesUseCase(_state.value.images) }
- 尝试通过图片URI从文件系统获取字节数据:
override suspend fun getFile(uri: String): ByteArray = withContext(coroutineContext) { File(Uri.parse(uri).path).readBytes() }
此步骤应用崩溃,抛出异常:
java.io.FileNotFoundException: /picker/0/com.android.providers.media.photopicker/media/1000003737: open failed: ENOENT (No such file or directory)
已在Manifest中添加以下权限,但问题仍未解决:
READ_EXTERNAL_STORAGEREAD_MEDIA_IMAGESREAD_MEDIA_VISUAL_USER_SELECTED
问题
仅通过图片URI时,如何正确从文件系统获取图片的字节数据?
解决方法
照片选择器返回的是Content URI,并非真实的文件系统路径,直接通过Uri.path创建File对象必然找不到文件。正确的做法是借助ContentResolver打开输入流读取字节数据:
修正后的getFile方法
需要传入Context(建议用Application Context避免内存泄漏)来获取ContentResolver:
override suspend fun getFile(context: Context, uri: String): ByteArray = withContext(Dispatchers.IO) { val contentUri = Uri.parse(uri) context.contentResolver.openInputStream(contentUri)?.use { inputStream -> inputStream.readBytes() } ?: throw IOException("无法打开图片输入流") }
关键说明
- ContentResolver的作用:系统通过
ContentResolver统一处理Content URI的访问逻辑,自动适配权限和文件的实际存储位置,无需关心真实路径。 - IO线程执行:文件读取属于耗时IO操作,必须切换到
Dispatchers.IO线程,避免阻塞主线程。 - 自动关闭流:用
use函数包裹输入流,能确保操作完成后自动释放资源,避免内存泄漏。 - 权限适配:API 34下,
READ_MEDIA_VISUAL_USER_SELECTED已经足够授权访问用户通过照片选择器选中的图片,无需额外申请其他存储权限。
调用示例
在ViewModel中调用时,传入Application Context:
private fun sendImages() = viewModelScope.launch { val imageBytesList = _state.value.images.map { uri -> useCases.getFile(getApplication(), uri) } // 后续将imageBytesList发送至服务器 }
内容的提问来源于stack exchange,提问作者qveex
相关产品推荐
相关产品推荐

