Android 10下外部SD卡文件创建与写入权限问题求助
解决Android可移除SD卡写入权限拒绝问题(EACCES)
你遇到的问题核心在于:Android 10(API 29)及以上版本对可移除SD卡的写入权限有特殊限制,即使你拥有WRITE_EXTERNAL_STORAGE权限并开启了requestLegacyExternalStorage,也无法直接写入可移除SD卡的根目录或非应用专属文件夹——但读取不受此限制,这就是为什么你的读功能正常、写功能失败的原因。
原因拆解
WRITE_EXTERNAL_STORAGE权限在Android 10+仅对内置外部存储(/storage/emulated/0)的公共目录有效,对可移除SD卡的共享目录完全失效。requestLegacyExternalStorage="true"是兼容Android 10的临时方案,但同样只作用于内置外部存储,无法覆盖可移除SD卡的写入限制。- 可移除SD卡的根目录(如
/storage/3066-3133)属于系统级共享存储区域,写入需要用户手动授权目录访问权限。
解决方案
方案1:使用存储访问框架(SAF)获取目录授权(推荐)
这是Android官方推荐的访问可移除SD卡共享目录的方式,需要让用户手动选择目标目录并授予读写权限:
- 启动SAF目录选择器
private static final int REQUEST_CODE_SD_DIR = 2; // 调用此方法启动目录选择 private void openSDDirectoryPicker() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); startActivityForResult(intent, REQUEST_CODE_SD_DIR); }
- 处理授权结果并写入文件
在onActivityResult中获取授权的目录Uri,然后用ContentResolver写入文件(替代原来的FileWriter):
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_SD_DIR && resultCode == RESULT_OK && data != null) { Uri treeUri = data.getData(); // 持久化权限,重启应用后依然有效 getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 写入title.txt到授权的SD卡目录 writeFileWithSAF(treeUri, "title.txt", " write text "); } } private void writeFileWithSAF(Uri treeUri, String fileName, String content) { DocumentFile rootDir = DocumentFile.fromTreeUri(this, treeUri); if (rootDir == null) return; DocumentFile targetFile = rootDir.findFile(fileName); if (targetFile == null) { targetFile = rootDir.createFile("text/plain", fileName); } try (OutputStream outputStream = getContentResolver().openOutputStream(targetFile.getUri())) { outputStream.write(content.getBytes()); popupToast("write success"); } catch (IOException e) { e.printStackTrace(); popupToast("write fail"); } }
方案2:写入SD卡的应用专属目录(无需额外授权)
如果你的业务允许将文件放在应用私有目录下,可以直接写入可移除SD卡的Android/data/[你的包名]/files/目录,这个目录属于应用私有空间,无需额外权限:
修改你的writeFile方法:
public void writeFile() { String fileTitle = "title.txt"; // 获取SD卡应用专属目录,下标1对应可移除SD卡,需先判断是否存在 File[] externalFilesDirs = getExternalFilesDirs(null); if (externalFilesDirs.length < 2 || externalFilesDirs[1] == null) { popupToast("SD card not available"); return; } File sdAppDir = externalFilesDirs[1]; File file = new File(sdAppDir, fileTitle); try { if (!file.exists()) { file.createNewFile(); } FileWriter writer = new FileWriter(file, false); String str = " write text "; writer.write(str); writer.close(); popupToast("write success"); } catch (IOException e) { e.printStackTrace(); popupToast("write fail"); } }
⚠️ 注意:卸载应用时,这个目录下的文件会被自动删除。
方案3:升级到targetSdk 31并申请MANAGE_EXTERNAL_STORAGE权限(不推荐)
如果你的应用是文件管理器类的应用,可以考虑升级到targetSdk 31,然后申请MANAGE_EXTERNAL_STORAGE权限,这个权限允许完全访问所有存储设备,但需要在应用商店说明用途,且可能无法通过审核。
额外检查点
- 确认
build.gradle中的targetSdkVersion确实是29,同步项目后生效。 - 检查设备是否开启了SD卡的物理写保护(部分SD卡有实体开关)。
内容的提问来源于stack exchange,提问作者Redwings
相关产品推荐
相关产品推荐

