You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android11文件读写报错open failed: EACCES (Permission denied)问题问询

问题原因

  • Android 11(API 级别 30)起强制执行分区存储机制,Android 10上生效的android:requestLegacyExternalStorage="true"配置在API30+系统中完全失效,普通应用默认无法直接通过绝对路径访问公共目录下的第三方文件
  • 你当前读取的/storage/emulated/0/Documents/下的文件属于公共存储目录,普通应用即使申请了READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限,也无法直接通过File API访问其他应用生成的公共目录文件,所以抛出EACCES权限拒绝错误
  • MANAGE_EXTERNAL_STORAGE权限属于特殊权限,仅文件管理器、备份类等特定用途应用可以通过Google Play审核,普通应用申请确实无法过审

合规适配方案(无需申请特殊权限,可正常过审)

以下方案覆盖三类常见使用场景,你可以根据自己的业务需求选择:

场景1:json文件是应用自身生成的配置文件

如果该文件是你的应用生成的,优先存储到应用私有目录,完全不需要申请存储权限,全版本Android系统都可以正常读写:

  • 私有文件目录获取方式:context.getExternalFilesDir(null),对应路径为/storage/emulated/0/Android/data/[你的应用包名]/files/
  • 修改后的读取代码参考:
public static JSONObject loadJSONFromAsset(Context context, String relativePath) {
    JSONObject json = null;
    // 从应用私有目录读取,无需权限
    File rootDir = context.getExternalFilesDir(null);
    File file = new File(rootDir, relativePath);
    try {
        FileInputStream is = new FileInputStream(file);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        json = new JSONObject(new String(buffer, "UTF-8"));
    } catch (IOException | JSONException ex) {
        ex.printStackTrace();
        return null;
    }
    return json;
}

调用时仅需传入相对路径即可,比如"configs/database.json",不需要传入完整绝对路径。

场景2:json文件是用户从外部导入的

如果需要让用户主动选择设备上的json文件导入,使用系统存储访问框架(SAF),不需要申请任何存储权限:

  1. 启动系统文件选择器让用户选中目标json文件:
private static final int REQUEST_CODE_PICK_JSON = 1001;

// 触发文件选择的逻辑
private void pickJsonFile() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/json");
    startActivityForResult(intent, REQUEST_CODE_PICK_JSON);
}
  1. 接收选择结果后读取文件内容:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_PICK_JSON && resultCode == RESULT_OK && data != null) {
        Uri fileUri = data.getData();
        try {
            InputStream is = getContentResolver().openInputStream(fileUri);
            // 后续读取逻辑和原有逻辑一致,读取完流转JSONObject即可
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

场景3:json是应用预内置的资源文件

如果该json是打包在APK的assets目录里的预制配置,直接用AssetManager读取即可,不需要任何权限:

public static JSONObject loadJSONFromAsset(Context context, String assetFileName) {
    JSONObject json = null;
    try {
        InputStream is = context.getAssets().open(assetFileName);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        json = new JSONObject(new String(buffer, "UTF-8"));
    } catch (IOException | JSONException ex) {
        ex.printStackTrace();
        return null;
    }
    return json;
}

内容的提问来源于stack exchange,提问作者Ian Pablo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:00:04