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

Android使用Apache POI读取Excel报FileNotFoundException排查

问题根因

代码存在5处核心错误导致文件找不到、读取失败:

  • 变量遮蔽:onActivityResult回调内重新定义了局部变量Uri uri = null,类的全局成员uri始终未被赋值,后续打开文件时拿不到正确的文件地址
  • 静态方法非法调用:readExcelFromStorage被定义为静态方法,方法内直接调用非静态的getContentResolver()本身就会编译/运行错误,且方法未接收正确的Uri参数,完全依赖全局空Uri
  • 格式硬编码:所有文件都用HSSFWorkbook初始化,该类仅支持.xls格式,读取.xlsx文件会直接抛异常
  • 冗余权限逻辑:使用ACTION_OPEN_DOCUMENT获取的content类型Uri自带系统临时访问授权,不需要申请READ_EXTERNAL_STORAGE权限,高版本Android上该权限已废弃,反而会导致授权逻辑失效
  • 基础逻辑笔误:文件名分割正则多写了转义符、路径截取判断条件写错,会导致文件格式判断失效
修复方案

按以下步骤修改代码即可解决问题:

  1. 修复Uri变量遮蔽问题
    删掉onActivityResult回调内的Uri uri = null;局部变量定义,直接给类的全局uri成员赋值,修正文件名截取、格式判断的笔误,拿到合法Excel文件后直接调用读取方法,移除多余的权限申请步骤。
  2. 重构文件读取方法
    去掉readExcelFromStorage的static修饰符,将文件Uri、文件后缀作为参数传入,根据文件后缀选择对应的POI Workbook实现类,通过传入的Context打开文件输入流。
  3. 补全POI依赖
    读取xlsx格式需要额外引入poi-ooxml包,否则会报类找不到错误。
修正后核心代码

ActivityResult回调部分

ActivityResultLauncher<Intent> launch_activity = registerForActivityResult
        (new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Intent data = result.getData();
                            // 删掉局部uri定义,直接给全局成员赋值
                            uri = null;
                            if (data != null) {
                                uri = data.getData();
                            }
                            if (uri != null && uri.getScheme().equals("content")) {
                                try (Cursor returnCursor = getContentResolver().query(uri, null, null, null, null)) {
                                    if (returnCursor != null && returnCursor.moveToFirst()) {
                                        name = returnCursor.getString(returnCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
                                    }
                                }
                            }
                            if(name == null){
                                if (uri != null) {
                                    name = uri.getPath();
                                }
                                int cut = -1;
                                if (name != null) {
                                    cut = name.lastIndexOf('/');
                                }
                                if(cut != -1){
                                    if (name != null) {
                                        name = name.substring(cut + 1);
                                    }
                                }
                            }
                            String[] extension = null;
                            if (name != null) {
                                // 修正分割点号的正则
                                extension = name.split("\\.");
                            }
                            if (extension != null && (extension[extension.length - 1].equals("xls") || extension[extension.length - 1].equals("xlsx"))) {
                                // 不需要申请权限,直接调用读取方法
                                readExcelFromStorage(MainActivity.this, uri, extension[extension.length - 1]);
                            } else if (extension != null) {
                                Toast.makeText(MainActivity.this,extension[extension.length - 1]+" 格式文件不支持",Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                });

文件读取方法

// 去掉static修饰,传入context、文件uri、文件后缀
public void readExcelFromStorage(Context context, Uri fileUri, String fileExtension) {
    InputStream fileInputStream = null;    
    Workbook workbook = null;
    try {
        fileInputStream = context.getContentResolver().openInputStream(fileUri);
        if (fileInputStream == null) {
            Toast.makeText(context, "文件打开失败", Toast.LENGTH_SHORT).show();
            return;
        }

        // 根据后缀选择对应Workbook实现
        if ("xls".equals(fileExtension)) {
            workbook = new HSSFWorkbook(fileInputStream);
        } else if ("xlsx".equals(fileExtension)) {
            workbook = new XSSFWorkbook(fileInputStream);
        }

        if (workbook == null) return;
        Sheet sheet = workbook.getSheetAt(0);
        if (sheet == null) return;

        // 遍历行读取单元格
        for (Row row : sheet) {
            // 跳过表头
            if (row.getRowNum() == 0) continue;
            Iterator<Cell> cellIterator = row.cellIterator();
            while (cellIterator.hasNext()) {
                Cell cell = cellIterator.next();
                // 高版本POI用枚举判断单元格类型,兼容新旧版本写法
                switch (cell.getCellType()) {
                    case NUMERIC:
                        Toast.makeText(context.getApplicationContext(), String.valueOf(cell.getNumericCellValue()),Toast.LENGTH_SHORT).show();
                        break;
                    case STRING:
                        Toast.makeText(context.getApplicationContext(), cell.getStringCellValue(),Toast.LENGTH_SHORT).show();
                        break;
                    default:
                        break;
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
        Toast.makeText(context, "读取失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    } finally {
        try {
            if (fileInputStream != null) fileInputStream.close();
            if (workbook != null) workbook.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Gradle依赖配置

在模块级build.gradle的dependencies节点添加POI依赖:

dependencies {
    // 其他原有依赖保留
    implementation 'org.apache.poi:poi:5.2.3'
    implementation 'org.apache.poi:poi-ooxml:5.2.3'
}
额外说明

原代码中checkPermission、onRequestPermissionsResult里和存储权限申请、权限通过后调用读文件的逻辑可以直接删除,ACTION_OPEN_DOCUMENT返回的Uri由系统提供临时读写权限,不需要申请任何存储权限,在Android 10~14全版本都可以正常访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:09:21