本文记录一下用Apache POI操作Excel时可能遇到一些坑。
excel版本错误
首先是excel版本问题,由于office excel 03版和07版的区别,在读取文件时如果版本不匹配会报以下错误。
org.apache.poi.poifs.filesystem.NotOLE2FileException: Invalid header signature; read 0x0010000000060809, expected 0xE11AB1A1E011CFD0 - Your file appears not to be a valid OLE2 document
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:167)
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:106)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:137)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:382)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:364)
最初解决办法是根据文件名采用不同的Workbook来初始化stream,大致代码如下,请忽视判断逻辑不严谨的问题(这里应该取后缀名而不是判断包含)。
if (filePath.indexOf(".xlsx") != -1) {
XSSFWorkbook wb = new XSSFWorkbook(inputStream);
return wb.getSheetAt(sheetIndex);
} else {
HSSFWorkbook wb = new HSSFWorkbook(inputStream);
return wb.getSheetAt(sheetIndex);
}
手动修改文件名后不匹配
通过上面的方法大部分还是可以解决的,但是遇到人为改变后缀名后会出现如下异常。
Caused by: org.apache.poi.openxml4j.exceptions.InvalidFormatException: Package should contain a content type part [M1.13]
at org.apache.poi.openxml4j.opc.ZipPackage.getPartsImpl(ZipPackage.java:203)
at org.apache.poi.openxml4j.opc.OPCPackage.getParts(OPCPackage.java:684)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:275)
at org.apache.poi.util.PackageHelper.open(PackageHelper.java:37)
再次优化一下,使用工厂模式创建Workbook:
Workbook workbook = WorkbookFactory.create(inputStream);
看一下POI的源码:
public static Workbook create(InputStream inp, String password) throws IOException, InvalidFormatException, EncryptedDocumentException {
InputStream is = FileMagic.prepareToCheckMagic(inp);
FileMagic fm = FileMagic.valueOf(is);
switch(fm) {
case OLE2:
NPOIFSFileSystem fs = new NPOIFSFileSystem(is);
return create(fs, password);
case OOXML:
return new XSSFWorkbook(OPCPackage.open(is));
default:
throw new InvalidFormatException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
可以看到他是根据stream类型来初始化的,这样即使人为改变文件名也无关紧要。
stream类型错误
通过上面的方式处理后,有时候还是会出现以下异常:
org.apache.poi.openxml4j.exceptions.InvalidFormatException: Your InputStream was neither an OLE2 stream, nor an OOXML stream
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:186)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:149)
at com.github.crab2died.ExcelUtils.readExcel2Objects(ExcelUtils.java:126)
at com.github.crab2died.ExcelUtils.readExcel2Objects(ExcelUtils.java:206)
这个问题可能是文件传输stream为非可读性的stream,解决方法为将读取的stream转为ByteArrayInputStream再读取:
InputStream is = new ByteArrayInputStream(xlsxInByteArray);
然而笔者是直接读取本地文件,并非此类问题,以下解答给了笔者答案,直接文件另存为后再读取新的文件,问题解决。
https://stackoverflow.com/questions/29894185/your-inputstream-was-neither-an-ole2-stream-nor-an-ooxml-stream-while-reading
如果文件很多,直接在程序中先另写一个文件再读取新文件即可,目前没有找到更好的解决方案。