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

Android中如何获取content://media格式的音频文件Content URI

问题原因

你用ActivityResultContracts.GetContent()获取到的URI来自Downloads Documents Provider,而非系统媒体库的MediaStore Provider。这是因为文件选择器允许用户从任意存储位置(比如下载文件夹、第三方文件管理器)选择文件,不同来源的文件对应不同的Content Provider,URI格式自然不同。你需要的content://media/internal/audio/media/0是MediaStore中音频条目的标准URI,仅对应系统媒体库索引的音频文件。

解决方法

方法1:将现有URI转换为MediaStore音频URI

如果用户选择的文件已经被MediaStore索引,可以通过ContentResolver查询匹配的音频条目,获取对应的MediaStore URI:

fun getMediaStoreAudioUri(contentResolver: ContentResolver, sourceUri: Uri): Uri? {
    val filePath = getFilePathFromUri(contentResolver, sourceUri) ?: return null

    val projection = arrayOf(MediaStore.Audio.Media._ID)
    val selection = "${MediaStore.Audio.Media.DATA} = ?"
    val selectionArgs = arrayOf(filePath)

    contentResolver.query(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
    )?.use { cursor ->
        if (cursor.moveToFirst()) {
            val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID))
            return Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id.toString())
        }
    }
    return null
}

// 辅助方法:从任意URI获取文件路径(适配不同Provider)
private fun getFilePathFromUri(contentResolver: ContentResolver, uri: Uri): String? {
    when (uri.scheme) {
        "content" -> {
            val projection = arrayOf(MediaStore.Images.Media.DATA)
            contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
                if (cursor.moveToFirst()) {
                    return cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
                }
            }
            // 处理Downloads Provider的特殊情况
            if (uri.authority == "com.android.providers.downloads.documents") {
                val id = DocumentsContract.getDocumentId(uri)
                val contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    id.toLong()
                )
                return getFilePathFromUri(contentResolver, contentUri)
            }
        }
        "file" -> {
            return uri.path
        }
    }
    return null
}

在你的onResult中调用这个方法:

onResult = { uri -> 
    uri?.let {
        val mediaUri = getMediaStoreAudioUri(contentResolver, it)
        // mediaUri就是你需要的content://media/...格式
    }
}

注意:这个方法仅对已经被MediaStore索引的文件有效,如果用户选择的是未被媒体库扫描到的文件(比如刚下载还未被索引的音频),会返回null。

方法2:直接从MediaStore选择音频

如果你只需要用户从系统媒体库中选择音频,可以替换GetContent()为ACTION_PICK对应的ActivityResultContract,直接获取MediaStore格式的URI:

val audioPicker = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val mediaUri = result.data?.data
        // 这里得到的就是content://media/internal/audio/media/0这类格式的URI
    }
}

// 调用方式
val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)
audioPicker.launch(pickIntent)

这种方法的局限性是:用户只能从系统媒体库选择音频,无法选择下载文件夹、SD卡根目录等位置的未索引文件。

注意事项
  • Android 10及以上版本的Scoped Storage限制,直接获取文件路径可能会失败,建议优先使用ContentResolver操作URI,而非依赖文件路径。
  • 如果必须处理未被MediaStore索引的文件,你可能需要复制文件到应用私有目录,再将其插入MediaStore获取对应的URI。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 03:42:15