Android保存PDF报EPERM错误 已申请读写权限仍触发FileNotFoundException如何解决
安卓PDF存储EPERM异常解决方案
核心问题根因
- 权限申请是异步操作:你当前调用
checkPermission后直接执行文件写入逻辑,此时系统还未完成权限审批,调用写文件逻辑时自然没有存储权限 - 分区存储适配问题:如果你的App
targetSdkVersion >= 29(安卓10及以上),系统默认开启分区存储限制,禁止直接写入外部存储根目录Environment.getExternalStorageDirectory(),哪怕申请了存储权限也会被拒绝 - 权限规则变更:安卓13(API 33)及以上版本,
WRITE_EXTERNAL_STORAGE权限已经完全废弃,申请该权限不会有任何效果
修复步骤
1. 处理权限申请异步逻辑
将文件写入逻辑封装为独立方法,在系统权限回调onRequestPermissionsResult中判断权限申请通过后,再执行写入逻辑,不要申请权限后立刻执行写入。
2. 适配分区存储规则
二选一即可:
- 方案1(优先推荐,无需申请权限):将PDF存入应用专属外部目录,该目录只有你的App可以访问,不需要申请任何存储权限。路径通过
getExternalFilesDir(null)获取,对应存储位置为/storage/emulated/0/Android/data/你的应用包名/files/ - 方案2(需要存入公共目录用户可见):使用
MediaStoreAPI写入公共的下载/文档目录,不要直接使用File路径操作公共存储区域。 - 临时过渡方案:如果需要快速兼容,可在
AndroidManifest.xml的application节点添加属性android:requestLegacyExternalStorage="true",可在安卓10设备上绕过分区存储限制,该方案在安卓11及以上已经失效。
3. 适配不同版本权限规则
- 运行安卓9及以下(API <=28):正常申请
READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限 - 运行安卓10-12(API 29-32):申请
READ_EXTERNAL_STORAGE即可,写权限不需要单独申请 - 运行安卓13及以上(API >=33):如果只是读写应用自身的文件,不需要申请任何存储权限;如果需要读取其他应用的媒体文件,按需申请
READ_MEDIA_*系列权限即可。
修改后代码示例
// 先申请权限,再在回调中执行写入 checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, 101); // 封装PDF写入逻辑 private void savePdf() { // 改成应用专属外部目录,不需要权限 File file = new File(getExternalFilesDir(null), "/myFile.pdf"); try { PdfReader reader = new PdfReader(getResources().openRawResource(R.raw.bail_1)); try { PdfWriter writer = new PdfWriter(new FileOutputStream(file)); PdfDocument pdf = new PdfDocument(reader, writer); pdf.close(); writer.close(); reader.close(); Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show(); } catch (FileNotFoundException e) { e.printStackTrace(); Toast.makeText(this,"FileNotFoundException: " + e.getMessage(), Toast.LENGTH_SHORT).show(); return; } } catch (IOException e) { e.printStackTrace(); Toast.makeText(this,"IOException: " + e.getMessage(), Toast.LENGTH_SHORT).show(); return; } } // 重写权限申请回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 101) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限申请通过,执行写入 savePdf(); } else { Toast.makeText(this, "需要存储权限才能保存文件", Toast.LENGTH_SHORT).show(); } } } private void checkPermission(String permission, int requestCode) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } else { // 已经有权限,直接执行写入 savePdf(); } }
内容的提问来源于stack exchange,提问作者Mama
相关产品推荐
相关产品推荐

