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

compileSdk 32下读取CSV文件遭遇EACCES权限拒绝问题求助

问题分析与解决方案

核心问题:错误的文件读取方式

你通过ActivityResultContracts.GetContent()获取的是内容URI,而非直接的文件系统路径。此时调用getRealPathFromURI()试图转换为真实文件路径,再用FileReader直接访问的方式,在Android 10(API 29)及以上版本会被系统权限拦截——哪怕开启了android:requestLegacyExternalStorage,这种直接访问文件路径的操作依然受限制,因为内容URI对应的文件可能不在应用沙箱范围内,且MediaStore.Files.FileColumns.DATA字段在Android Q之后已被废弃,getRealPathFromURI()拿到的路径本身就可能无效或无访问权限。

次要问题:权限请求的异步逻辑错误

点击FloatingActionButton时,你先调用requestPermission(),紧接着就检查权限状态并决定是否启动文件选择器。但权限请求是异步操作,调用后不会立刻完成授权,此时的权限检查结果还是之前的状态,可能导致权限未真正授权就执行后续操作(不过你的异常核心原因还是文件读取方式问题)。


修复方案

1. 改用ContentResolver读取文件内容

直接通过ContentResolver打开URI对应的输入流,无需转换为文件路径,这是Android 10+推荐的文件操作方式:

private void readCSV(Uri result) {
    Log.d(TAG, "readCSV: reading..");

    // 使用try-with-resources自动关闭流
    try (InputStream inputStream = getContentResolver().openInputStream(result);
         CSVReader reader = new CSVReader(new InputStreamReader(inputStream))) {
        String[] nextLine;
        while ((nextLine = reader.readNext()) != null) {
            Log.d(TAG, "readCSV: data is " + nextLine[0]);
        }
    } catch (IOException e) {
        Log.d(TAG, "readCSV: e " + e.getMessage());
    } catch (CsvValidationException e) {
        e.printStackTrace();
        Log.d(TAG, "readCSV: " + e.getMessage());
    }
}

同时删除无用的getRealPathFromURI()方法。

2. 修正权限请求的逻辑

将文件选择器的启动逻辑移到权限请求的回调中,确保权限授权完成后再执行:

floatingActionButton.setOnClickListener(view -> {
    requestPermission();
});

// 修改权限请求的回调逻辑
permissionResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
        result -> {
            Boolean isGranted = result.get(Manifest.permission.READ_EXTERNAL_STORAGE);
            isReadPermissionGranded = isGranted != null && isGranted;
            if (isReadPermissionGranded) {
                getCSV.launch("*/*");
            } else {
                Log.d(TAG, "onActivityResult: no permission granted");
            }
        });

// 调整权限请求方法
private void requestPermission() {
    List<String> permissionRequest = new ArrayList<>();
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        permissionRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE);
    }
    if (!permissionRequest.isEmpty()) {
        permissionResultLauncher.launch(permissionRequest.toArray(new String[0]));
    } else {
        // 权限已存在,直接启动文件选择器
        getCSV.launch("*/*");
    }
}

3. 额外说明

  • android:requestLegacyExternalStorage="true"仅在Android 10(API 29)生效,Android 11(API 30)及以上版本该属性被忽略,系统强制使用分区存储。所以即使你配置了这个属性,在compileSdk32环境下,直接访问外部文件路径依然会被限制,必须使用内容URI操作文件。
  • 若需适配Android 11+,建议完全遵循分区存储规范,避免依赖真实文件路径的操作。

内容的提问来源于stack exchange,提问作者Prasath S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:35:23