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

Android10/11下选取Excel文件Uri转File出现FileNotFoundException问题咨询

问题原因

  • ACTION_OPEN_DOCUMENT、ACTION_GET_CONTENT返回的是content://开头的内容URI,不是本地文件的真实物理路径,uri.getPath()拿到的是系统内容提供者的虚拟路径,直接用new File()访问当然找不到对应文件。
  • Android 10(API29)及以上版本已经限制了应用直接访问共享存储的文件路径,即便你通过其他方式拿到了文件的真实路径,也没有权限直接访问。你测试时静态路径能正常访问,是因为测试文件刚好在你应用有权限访问的目录,或者你申请了旧版的存储权限,无法适配所有用户场景。
  • 额外的逻辑问题:你给Intent设的setType("*/*")会允许用户选择所有类型文件,后续的Excel文件判断逻辑容错成本更高。

可行解决方案

方案1:修改Excel工具类支持输入流(推荐)

不需要把URI转成File对象,直接用系统提供的ContentResolver打开URI对应的输入流传给解析逻辑,是Android官方推荐的适配方式,性能最好,不需要额外拷贝文件。

第一步:优化文件选择器唤起逻辑

不需要分版本使用不同Action,统一用ACTION_OPEN_DOCUMENT适配所有Android 4.4及以上版本,同时限制只能选择Excel类型文件:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// 限制可选文件类型为Excel
intent.setType("*/*");
String[] mimeTypes = {"application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
// 申请读取权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, 1);

第二步:修改回调处理逻辑

先调整ExcelUtils.readExcel方法的参数,把原来的File类型改为InputStream,再修改回调代码:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1 && resultCode == Activity.RESULT_OK && data != null) {
        Uri uri = data.getData();
        if (uri == null) return;
        // 获取选中文件的文件名
        String fileName = null;
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
            cursor.close();
        }
        if (ExcelUtils.checkIfExcelFile(fileName)) {
            try {
                // 直接打开URI对应的输入流
                InputStream inputStream = getContentResolver().openInputStream(uri);
                excelUtils.readExcel(inputStream, FirstRowHeader);
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 如果需要长期访问该文件,可以保留你原有申请持久URI权限的逻辑
    }
}

方案2:拷贝文件到应用私有目录生成File

如果你不想修改原有ExcelUtils的代码,可以把选中的文件内容拷贝到你的应用私有目录(比如getCacheDir()、getFilesDir()),生成应用有权限直接访问的本地File后再调用原有方法:

// 在onActivityResult获取到uri后调用该方法得到本地File对象
private File getFileFromUri(Uri uri, String fileName) throws IOException {
    File outputFile = new File(getCacheDir(), fileName);
    InputStream inputStream = getContentResolver().openInputStream(uri);
    FileOutputStream outputStream = new FileOutputStream(outputFile);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) > 0) {
        outputStream.write(buffer, 0, length);
    }
    inputStream.close();
    outputStream.close();
    return outputFile;
}

拷贝完成后直接用返回的File对象调用原有readExcel方法即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 12:45:02