Android 11及以上系统写入内部存储Movies目录CSV文件报EPERM错误如何解决
报错原因
Android 11(API 30)及以上系统强制执行分区存储规则,常规WRITE_EXTERNAL_STORAGE权限在API 30以上已经失效,第三方应用无法直接通过File API读写公共存储目录下的非自有文件,直接构造File对象写入公共Movies目录就会触发EPERM(操作不允许)异常。
解决步骤
1. 配置AndroidManifest.xml权限
添加适配不同系统版本的权限声明:
<!-- 适配Android 12及以下的存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- 适配Android 13及以上的媒体读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <application ... <!-- 适配Android 10的兼容配置,Android 11及以上不生效 --> android:requestLegacyExternalStorage="true"> ... </application>
2. 动态申请权限
在页面启动时主动申请对应存储权限,用户授权后再执行文件写入操作。
3. 使用MediaStore API执行文件写入(兼容全版本)
Android 11及以上必须通过MediaStore API操作公共媒体目录下的文件,替换你原有代码为如下实现即可:
// 适配API 30+写入公共Movies目录的逻辑 ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, "newFile.csv"); values.put(MediaStore.MediaColumns.MIME_TYPE, "text/csv"); // 指定文件存储路径为Movies/NewFolder values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/NewFolder/"); // 不需要重复创建同名文件可以加上这句,默认会自动对重名文件加数字后缀 // values.put(MediaStore.MediaColumns.IS_PENDING, 1); ContentResolver resolver = getContentResolver(); Uri uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values); if (uri == null) { // 文件创建失败的自定义处理逻辑 return; } try { OutputStream f = resolver.openOutputStream(uri); String title = "X Coord, Y Coord, Z Coord, Accelerometer\n"; f.write(title.getBytes()); f.flush(); f.close(); // 如果前面加了IS_PENDING配置,写入完成后需要更新文件状态 // values.clear(); // values.put(MediaStore.MediaColumns.IS_PENDING, 0); // resolver.update(uri, values, null, null); } catch (IOException e) { e.printStackTrace(); }
可选:特殊场景使用所有文件访问权限
如果你的应用是文件管理、备份类工具,需要自由访问所有存储目录,可以申请MANAGE_EXTERNAL_STORAGE权限,该权限需要跳转到系统设置页用户手动授权,且Google Play对该权限的使用有严格审核规则,普通应用不推荐使用。
内容的提问来源于stack exchange,提问作者MD ABDUL HADY
相关产品推荐
相关产品推荐

