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

Android 11设备无法从下载文件夹外路径选取上传文件问题求助

Android 11(API 30)强制开启分区存储机制,你当前代码中依赖查询MediaStore的_data字段获取文件绝对路径的方案已经被官方废弃,除Download目录、公共媒体库目录外的其他路径文件都无法通过该方式拿到本地路径,所以返回null。


方案1:放弃绝对路径转换,直接通过Uri处理文件(官方推荐)

你不需要拿到文件的真实本地路径,通过系统返回的Uri就可以读取到文件内容,你可以将Uri对应的文件拷贝到应用私有缓存目录,再用缓存文件的路径进行上传,适配所有Android版本,不需要申请额外存储权限。

新增工具方法:

// 从Uri获取文件名
private static String getFileName(Context context, Uri uri) {
    String fileName = null;
    if (DocumentsContract.isDocumentUri(context, uri)) {
        String docId = DocumentsContract.getDocumentId(uri);
        String[] split = docId.split(":");
        fileName = split[split.length - 1];
    } else if ("content".equals(uri.getScheme())) {
        try (Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                int index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
                if (index != -1) {
                    fileName = cursor.getString(index);
                }
            }
        }
    }
    if (fileName == null) {
        fileName = uri.getLastPathSegment();
    }
    return fileName;
}

// 将Uri对应文件拷贝到应用私有缓存目录,返回缓存文件路径
public static String getPathFromUri(Context context, Uri uri) throws IOException {
    String fileName = getFileName(context, uri);
    File cacheFile = new File(context.getCacheDir(), fileName);
    try (InputStream inputStream = context.getContentResolver().openInputStream(uri);
         OutputStream outputStream = new FileOutputStream(cacheFile)) {
        byte[] buffer = new byte[4096];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, len);
        }
    }
    return cacheFile.getAbsolutePath();
}

修改MainActivity的onActivityResult逻辑:

将原来的

path = AppUtils.getfilePath(getContext(), uri);

替换为

try {
    path = AppUtils.getPathFromUri(getContext(), uri);
} catch (IOException e) {
    e.printStackTrace();
    Toast.makeText(getContext(), "读取文件失败", Toast.LENGTH_SHORT).show();
}

原有后续上传逻辑完全不用改动,直接使用返回的缓存路径即可。


方案2:申请全量存储权限(仅适合文件管理类应用)

如果你的应用确实需要访问设备所有存储路径,可以申请全量存储权限解决问题,该权限Google Play上架需要额外审核,非必要不推荐。

  1. 修改AndroidManifest.xml配置:
<!-- Android 11及以上全存储权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="29" />

<application
    ...
    android:requestLegacyExternalStorage="true">
  1. 代码中动态申请权限:
// 申请全量存储权限请求码,自己定义即可
private static final int REQUEST_MANAGE_STORAGE = 1001;

// 权限判断逻辑
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQUEST_MANAGE_STORAGE);
    }
}

用户授权后原有获取绝对路径的逻辑即可正常使用。


内容的提问来源于stack exchange,提问作者Ajay Ghatge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 14:54:03