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

Android Compose中通过URI获取选中媒体文件字节的方法(API34测试)

测试环境:API 34
需求

获取用户选择的照片并发送至服务器

我的实现方案
  1. 通过Compose的ActivityResultContracts.PickMultipleVisualMedia调用用户相册选择照片:
val photoPicker = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.PickMultipleVisualMedia(12)
) { photos ->
    photos.takeIf { it.isNotEmpty() }?.let { uri ->
        dispatch(Action.OnClickUploadImages(uri.map { it.toString() }))
    }
}
  1. 将URI列表保存至ViewModel,通过Coil展示图片(此步骤正常运行)
  2. 用户点击提交按钮时,将图片发送至服务器:
private fun sendImages() = viewModelScope.launch {
    useCases.sendImagesUseCase(_state.value.images)
}
  1. 尝试通过图片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_STORAGE
  • READ_MEDIA_IMAGES
  • READ_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("无法打开图片输入流")
}

关键说明

  1. ContentResolver的作用:系统通过ContentResolver统一处理Content URI的访问逻辑,自动适配权限和文件的实际存储位置,无需关心真实路径。
  2. IO线程执行:文件读取属于耗时IO操作,必须切换到Dispatchers.IO线程,避免阻塞主线程。
  3. 自动关闭流:用use函数包裹输入流,能确保操作完成后自动释放资源,避免内存泄漏。
  4. 权限适配: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:59:57