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目录新建文件的逻辑,在满足以下条件时是可用的:
- 仅用于读取你当前应用刚刚新建的这个saves.zip文件,写入后直接用返回的
itemUri调用getContentResolver().openInputStream(itemUri)读取是没有权限问题的 - 需要补充Android 10以下版本的兼容逻辑,否则低于API 29的设备上
uri会为空导致崩溃 - 存在一个隐患:如果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,不能直接后台读取,必须引导用户主动选中文件获取权限:
- 调用系统文件选择器
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);
- 接收选中结果读取文件
@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
相关产品推荐
相关产品推荐

