Android保存文件到公共存储报Operation not permitted错误,已申请权限仍无效如何解决?
问题原因
- 分区存储限制:你运行App的设备系统是Android 11(API 30)及以上版本,且App目标SDK版本≥30时,传统的
WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE权限已经失效,系统强制启用分区存储,普通应用无法直接通过File API操作公共存储目录的文件,报错日志中Compat change id reported: 147798919就是分区存储规则生效的标识。 - 文件名包含非法字符:你生成的文件名用了
ddMMyyyy HH:mm:ss的时间格式,其中冒号:是Android文件系统禁止使用的文件名特殊字符,也会触发创建文件失败的错误。 - 运行时权限逻辑不完整:你添加的运行时权限申请仅执行了权限校验和申请操作,没有处理用户同意权限后的回调逻辑,权限申请通过后也不会自动触发文件创建流程。
解决方案
方案1:使用官方推荐的MediaStore实现(无需申请特殊权限,适配所有新版本系统)
这是Android官方推荐的适配方案,不需要申请存储权限,就可以把文件写入公共Download目录且在文件管理器可见,示例代码如下:
public void gen(View view){ Log.i("CLICKED", "CLICKED"); Date date = new Date(); // 修正文件名格式,去掉非法的冒号 SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy HH_mm_ss"); String dateTime = sdf.format(date); Log.i("DATE", dateTime); String fileName = "TEXT" + dateTime + ".txt"; ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain"); // 指定写入公共Download目录 values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); ContentResolver resolver = getContentResolver(); Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri != null) { try { OutputStream outputStream = resolver.openOutputStream(uri); // 这里可以写入你的文件内容 outputStream.write("测试内容".getBytes()); outputStream.close(); Toast.makeText(getApplicationContext(), "文件创建成功", Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "文件创建失败", Toast.LENGTH_LONG).show(); } } }
同时可以删除manifest中原来申请的WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE权限(如果你的App不需要读取其他应用的文件的话)。
方案2:临时兼容方案(仅适合自用/不上架应用)
如果不需要上架应用市场,可以用以下兼容方式快速解决:
- 若目标SDK是Android 10(API 29),在manifest的
application标签中添加属性:
android:requestLegacyExternalStorage="true"
即可启用旧版存储逻辑,配合你之前写的运行时权限申请即可正常创建文件。
2. 若目标SDK是Android 11(API30)及以上,可以申请所有文件访问权限,在manifest中添加:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
运行时跳转权限设置页让用户手动开启权限,该权限需要应用市场审核,不建议上架应用使用。
必要修正
不管用哪种方案,都需要把时间格式中的冒号替换为下划线、横杠等合法字符,避免非法文件名导致的创建失败。
内容的提问来源于stack exchange,提问作者W Simm
相关产品推荐
相关产品推荐

