1、RecordInputStream$LeftoverDataException是什么异常?
最近在一个项目中使用 Apache POI 的 HSSF 模型读取 .xls
文件进行Excel解析时,用户上传的一个文件触发了如下Java异常:
1 | org.apache.poi.hssf.record.RecordInputStream$LeftoverDataException: |
这里发现 FontRecord
(ID=0x31)像是和字体有关。
经过网上搜索,发现也有人在解析 SelectionRecord
或 NumberRecord
时遭遇类似的“剩余字节”异常,例如 record ID 0x85
出现剩余 18 字节抛出同样异常。
此外还有报告出现 NumberRecord
(ID=0x203)剩余 4 字节的情况,均触发 RecordInputStream$LeftoverDataException
,参考: github.com[2]
2、为什么会触发RecordInputStream$LeftoverDataException?
2.1 异常原理
查看Apache POI的doc文档,可以发现,Apache POI在读取 BIFF 记录时会根据记录头中的长度精确消费字节,若读取后流中仍有未读字节,则触发 RecordInputStream$LeftoverDataException
。 [3]
HSSF 的 RecordInputStream.hasNextRecord()
会校验是否有剩余字节,并在发现不匹配时通过抛出此异常保护后续记录解析[4]。
2.2 触发场景
部分第三方工具或系统导出的 Excel 文件在写入 FontRecord
或其他记录时插入了“扩展”字节数据,导致 Apache POI 无法识别[5]。
如何解决问题呢?其实很简单,手动用 Microsoft Excel 或 WPS 打开并“另存为”后,通常可清除不兼容的扩展数据,从而避免该Java异常。
2.3 官方Bug修复进度如何?
Apache Bugzilla 中的 Bug 68854 专门报告了 FontRecord
剩余 4 字节的问题,并被标记为重复 Bug 60833。[6]
Bug 60833 自 2017 年以来一直未在官方版本中彻底修复,社区仍在讨论该边缘场景的处理方案。
3、如何解决RecordInputStream$LeftoverDataException问题?
3.1 另存为 Excel
最简单的方式是手动或脚本化地使用 Excel/WPS 对 .xls
文件执行“另存为”操作,该过程会重写文件结构并剔除多余字节,从而避免此Java异常[7]。
3.2 转为 .xlsx
将 .xls
文件转换为 Office Open XML(.xlsx
)格式,使用 XSSF 解析器可绕开 BIFF 的二进制校验机制,从而彻底规避该问题。
3.3 升级或打补丁
当然,更进一步的是等待官方修复了。如果等不及,可以自己动手在本地对 Apache POI 源码中的 FontRecord.readFields
或 RecordInputStream.hasNextRecord
增加容错逻辑,捕获并忽略多余字节,从而避免在Excel解析时触发RecordInputStream$LeftoverDataException异常。
Refenceces
[How to resolve InputStream LeftoverDataException : Initialisation of record 0x85 left 18 bytes remaining still to be read](https://stackoverflow.com/questions/50909356/how-to-resolve-inputstream-leftoverdataexception-initialisation-of-record-0x85) ↩︎
LeftoverDataException on reading xls file with HSSFWorkbook ↩︎
org.apache.poi.hssf.record.RecordInputStream$LeftoverDataException: Initialisation of record 0x31(FontRecord) left 4 bytes remaining still to be read. ↩︎
[Bug 60833] Initialisation of record 0x31 left 4 bytes remaining still to be read. ↩︎
POI处理导入excel发生RecordInputStream$LeftoverDataException处理记录 ↩︎