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

Android R读取内部Download文件夹文件报IOException访问拒绝问题咨询

Android 11(R)Download目录读取权限问题解决方案

问题根因

Android 11(API 30)开始强制执行分区存储机制:

  • android:requestLegacyExternalStorage="true" 属性仅对Android 10(API 29)生效,Android 11及以上系统会直接忽略该配置
  • READ_EXTERNAL_STORAGE 权限在Android 11及以上仅能访问公共目录下的图片、音频、视频三类媒体文件,无法直接访问Download目录下的zip、apk等非媒体类文件
  • 禁止所有应用直接通过File路径访问公共目录的非专属文件,所以你原来用FileInputStream读取Download路径的逻辑会抛出访问拒绝异常

无权限即可读取的文件夹范围

  • 应用内部专属目录:对应context.getFilesDir()、context.getCacheDir()路径,仅当前应用可访问,卸载应用时数据同步删除,无需任何权限
  • 应用外部专属目录:对应context.getExternalFilesDir()、context.getExternalCacheDir()路径,同样无需权限,卸载应用时数据同步删除
  • 若仅访问自身应用写入到公共目录的文件,无需额外申请权限,只需持有写入时返回的MediaStore Uri即可

你补充的代码是否正确?

你的这段代码是通过MediaStore在Download目录新建文件的逻辑,在满足以下条件时是可用的:

  1. 仅用于读取你当前应用刚刚新建的这个saves.zip文件,写入后直接用返回的itemUri调用getContentResolver().openInputStream(itemUri)读取是没有权限问题的
  2. 需要补充Android 10以下版本的兼容逻辑,否则低于API 29的设备上uri会为空导致崩溃
  3. 存在一个隐患:如果Download目录已经存在同名的saves.zip,系统会自动生成saves(1).zip这类带序号的新文件,不会覆盖原有文件,也读不到原有旧文件

代码优化点

ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, "saves.zip");
contentValues.put(MediaStore.Downloads.MIME_TYPE, "application/zip");
// 指定存储位置为Download根目录,可自定义子目录
contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
ZipInputStream zis = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    Uri uri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL);
    Uri itemUri = getContentResolver().insert(uri, contentValues);
    if (itemUri != null) {
        try {
            zis = new ZipInputStream(getContentResolver().openInputStream(itemUri));
            // 后续读流逻辑
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 用完关闭流
            if (zis != null) {
                try {
                    zis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
} else {
    // 低于Android 10版本可以直接用File路径读写
    File file = new File(Environment.getExternalStorageDirectory(), "Download/saves.zip");
    try {
        zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)));
        // 后续读流逻辑
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭流逻辑
    }
}

读取非自身应用写入的Download目录文件的正确方式

如果要读取其他应用存到Download里的saves.zip,不能直接后台读取,必须引导用户主动选中文件获取权限:

  1. 调用系统文件选择器
private static final int REQUEST_CODE_SELECT_ZIP = 1001;

// 启动选择器
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("application/zip");
intent.addCategory(Intent.CATEGORY_OPENABLE);
// 默认定位到Download目录
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, REQUEST_CODE_SELECT_ZIP);
  1. 接收选中结果读取文件
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_SELECT_ZIP && resultCode == RESULT_OK && data != null) {
        Uri zipUri = data.getData();
        if (zipUri != null) {
            try {
                ZipInputStream zis = new ZipInputStream(getContentResolver().openInputStream(zipUri));
                // 读流逻辑
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:不推荐申请MANAGE_EXTERNAL_STORAGE特殊权限,该权限需要用户手动到系统设置页开启,且普通应用上架谷歌应用商店时会被拒绝,仅文件管理器、备份类工具可申请使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 17:15:06