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

Android使用Multipart Retrofit上传文件时遭遇权限拒绝问题

问题解决:Android图库文件上传报EACCES权限拒绝

问题原因

从Android 10(API 29)开始引入的Scoped Storage(分区存储)机制,限制了应用直接访问外部存储的真实文件路径。即使你通过URI转换得到了文件路径,直接用File(path)打开文件也会被系统拒绝权限,抛出android.system.ErrnoException: open failed: EACCES (Permission denied)异常。此外,Android 13(API 33)之后,原有的READ_EXTERNAL_STORAGE权限被拆分为媒体类型专属权限,权限申请不匹配也会导致访问失败。

解决方案

1. 改用ContentResolver获取文件流,避免直接访问真实路径

放弃通过真实路径创建File对象,直接利用ContentResolver从URI获取文件输入流来构建MultipartBody.Part,这是Scoped Storage下的标准做法:

private fun getMultiPartFromUri(context: Context, uri: Uri, partName: String): MultipartBody.Part {
    val contentResolver = context.contentResolver
    // 获取文件MIME类型,默认用*/*
    val mimeType = contentResolver.getType(uri) ?: "*/*"
    
    val requestFile = object : RequestBody() {
        override fun contentType(): MediaType? {
            return mimeType.toMediaTypeOrNull()
        }

        override fun writeTo(sink: BufferedSink) {
            contentResolver.openInputStream(uri)?.use { inputStream ->
                sink.writeAll(inputStream.source())
            } ?: throw IOException("无法打开文件输入流")
        }
    }

    // 从URI获取文件名(可选,根据后端需求调整)
    val fileName = getFileNameFromUri(context, uri)
    return MultipartBody.Part.createFormData(partName, fileName, requestFile)
}

// 辅助方法:从媒体URI获取文件名
private fun getFileNameFromUri(context: Context, uri: Uri): String {
    var fileName = "unknown_file"
    val cursor = context.contentResolver.query(uri, null, null, null, null)
    cursor?.use {
        if (it.moveToFirst()) {
            val nameColumnIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
            if (nameColumnIndex != -1) {
                fileName = it.getString(nameColumnIndex)
            }
        }
    }
    return fileName
}

2. 确保权限申请正确

根据目标Android版本申请对应权限:

  • Android 13+(API 33):根据文件类型申请READ_MEDIA_IMAGES、READ_MEDIA_VIDEO或READ_MEDIA_AUDIO权限,在AndroidManifest.xml中声明:
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <!-- 根据需要添加其他媒体权限 -->
    
  • Android 10-12(API 29-32):申请READ_EXTERNAL_STORAGE权限:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
  • 所有版本都需要动态申请权限,确保用户授予权限后再进行文件操作。

3. 取消真实路径转换流程

直接保留从图库选择得到的Uri,不再转换为真实路径,全程用Uri处理文件上传,避免因路径转换带来的权限问题。

内容的提问来源于stack exchange,提问作者Shashank Singh Bisht

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:35:24