Android读取内部存储XLSX文件出现文件未找到及POI异常求解
问题根因
你碰到的崩溃根本不是文件找不到——报错栈已经显示,getContentResolver.openInputStream()已经成功返回了文件流,代码已经走到POI初始化xlsx解析器的阶段。崩溃的直接原因有三个:
- 安卓系统没有内置Java SE环境下的StAX(XML流式处理)实现,POI初始化时找不到
com.bea.xml.stream.EventFactory类,直接抛出配置错误 - 你之前以为的文件找不到错误,本质是代码里硬把content类型的Uri转本地文件路径的逻辑失效:安卓7.0之后通过存储访问框架(SAF)返回的Uri根本不支持直接转换为应用可访问的本地路径
- 日志里的
MQSService获取失败是小米定制系统的无关输出,和你的代码逻辑无关,无需处理
针对性解决步骤
1. 替换适配安卓的POI依赖
不要直接使用Java SE版本的POI依赖,否则一定会因为缺少安卓不兼容的系统类导致崩溃。在模块的build.gradle中引入以下依赖:
// 安卓适配版POI核心组件 implementation 'org.apache.poi:poi-android:5.2.3' // xlsx格式解析扩展 implementation 'org.apache.poi:poi-ooxml:5.2.3' // 补全缺失的StAX XML实现,解决EventFactory找不到的报错 implementation 'org.codehaus.woodstox:stax2-api:4.2.1' implementation 'com.fasterxml.woodstox:woodstox-core:6.4.0'
依赖配置完成后执行一次Clean Project操作,避免旧的缓存类文件导致类冲突。
2. 修正SAF文件读取的逻辑漏洞
你代码里通过data.getData().getPath()拿到的字符串不是真实的本地文件路径,硬走文件路径访问的方式必然会触发文件未找到错误。
正确的读取逻辑是:拿到Uri后直接通过ContentResolver把文件内容拷贝到应用私有缓存目录生成临时文件,再用临时文件初始化POI对象——既避开了Uri转路径的无效逻辑,也能避免直接传InputStream导致的大文件内存溢出问题:
// onActivityResult中拿到选中Uri后的处理逻辑 if (requestCode == 1 && resultCode == RESULT_OK && data != null && data.getData() != null) { Uri selectedUri = data.getData(); // 生成应用私有目录的临时文件 File tempExcelFile = new File(getCacheDir(), "temp_selected_excel.xlsx"); // 把选中的文件内容拷贝到临时文件 try (InputStream is = getContentResolver().openInputStream(selectedUri); FileOutputStream fos = new FileOutputStream(tempExcelFile)) { byte[] buffer = new byte[8192]; int readLen; while ((readLen = is.read(buffer)) != -1) { fos.write(buffer, 0, readLen); } } catch (IOException e) { e.printStackTrace(); return; } // 用临时文件读取xlsx内容 onReadClick(new FileInputStream(tempExcelFile)); }
3. 优化文件选择的Intent配置
把原来的全类型匹配的Intent,改成只匹配xlsx格式,避免用户误选其他格式文件导致解析错误:
if (v == btnChoose) { Intent pickFileIntent = new Intent(Intent.ACTION_GET_CONTENT); // 替换原来的.setType("*/*"),精准匹配xlsx格式 pickFileIntent.setType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); pickFileIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); pickFileIntent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(Intent.createChooser(pickFileIntent, "选择Excel文件"), 1); }
开发注意事项
- 不要为了获取本地文件路径去申请全量存储权限,SAF框架本身不需要申请存储权限,通过拷贝临时文件的方式完全可以满足读取需求
- 如果读取的xlsx文件超过10M,不要用XSSFWorkbook全量加载到内存,建议改用SAX流式解析,避免大文件导致应用崩溃
- 不要在读取流的过程中反复关闭、重开流,POI解析压缩格式的xlsx文件需要随机访问流内容,一次性把文件拷贝到本地临时文件的方式性能最优
内容的提问来源于stack exchange,提问作者Sagar Deshmukh
相关产品推荐
相关产品推荐

