POI报错:IllegalArgumentException: Your InputStream was neither an OLE2 stream, nor an OOXML stream

本文探讨了使用Spring MVC和POI处理上传的加密Excel文件时遇到的问题及解决办法。当使用公司电脑创建的加密Excel文件上传时,POI会因无法识别文件类型而报错。文章详细解释了错误原因,并提供了验证方法。

场景:上传excel文件,使用springMVC的MultipartFile接受文件流,再使用POI来处理流,但在使用creat的时候报错。

InputStream inputStream = file.getInputStream();
Workbook workbook = WorkbookFactory.create(inputStream);

错误信息

java.lang.IllegalArgumentException: Your InputStream was neither an OLE2 stream, nor an OOXML stream
	at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:75)
	at com.vispractice.vpshop.controller.admin.CoinCardCodeController.activateCardBatch(CoinCardCodeController.java:123)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
	
	......
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

原因:原来使用公司电脑创建的文件是加密的,所以POI处理不了。图标上有个加锁的标志:
在这里插入图片描述
验证:当我使用远程控制自己的电脑创建一个excel文件,再传到公司时,果然就不报错了。

具体分析:看POI源码可以看到它是根据文件的开始8位判断是否为excel文件的。这时候再对比加密和未加密文件的16进制头两个(也就是8个位):.

  • xlsx的是:cb
  • 加密后的xlsx是:30

结果也显而易见了,那就是加密后的excel文件POI把他判断成了非excel文件,当然,POI也没有解密的功能。

<think>我们正在解决一个文件导入失败的问题,错误提示为“InputStream 不是 OLE2OOXML 格式”。这意味着程序尝试读取一个文件,期望它是OLE2(如旧的.xls格式)或OOXML(如.xlsx格式)的Excel文件,但实际提供的流不符合这两种格式。 可能的原因包括: 1. 文件确实不是Excel文件(可能是文本文件、图片等)。 2. 文件扩展名与实际格式不匹配(例如,一个文本文件被命名为.xlsx)。 3. 文件损坏。 4. 读取文件的方式不正确,例如在读取之前文件流的位置已经被移动。 解决方案: 1. 确认文件格式:检查文件是否确实是Excel文件。可以尝试用Excel软件打开它,或者使用文本编辑器打开(如果是二进制文件,会看到乱码,但如果是文本格式如CSV,则可以看到明文)。 2. 验证文件头:OLE2格式的文件头为D0 CF 11 E0 A1 B1 1A E1(即魔术数字),而OOXML格式(xlsx)实际上是一个zip压缩包,其文件头为50 4B 03 04。我们可以通过读取文件的前几个字节来判断。 步骤: 1. 在尝试解析之前,先读取文件的前8个字节(或更多)并检查是否符合OLE2OOXML的签名。 2. 如果不符合,则说明文件格式错误,需要用户提供正确的文件。 3. 如果符合,再使用相应的解析器(如Apache POI)来读取。 另外,注意在读取文件流时,我们可能需要重置流的位置,因为检查文件头会消耗流的前几个字节。因此,在检查后,需要将流重置到起始位置,然后再交给解析器。 示例代码(Java): 假设我们有一个InputStream,我们可以使用以下方式检查: ```java import org.apache.poi.poifs.filesystem.FileMagic; import java.io.*; public boolean isExcelFormat(InputStream inputStream) throws IOException { // 使用POI的FileMagic来检测 FileMagic fm = FileMagic.valueOf(inputStream); if (fm == FileMagic.OLE2 || fm == FileMagic.OOXML) { return true; } return false; } ``` 但是,注意:在调用FileMagic.valueOf后,流的位置可能已经改变,因此我们需要使用支持mark/reset的流,或者在调用前重置流(如果可能的话)。 更好的做法是,在读取之前,将InputStream包装成BufferedInputStream,并标记,然后在检测后重置。 示例: ```java public void processFile(InputStream inputStream) throws IOException { // 为了支持mark/reset,我们包装成BufferedInputStream if (!inputStream.markSupported()) { inputStream = new BufferedInputStream(inputStream); } inputStream.mark(1024); // 标记当前位置,以便之后重置。读取不超过1024字节 // 检查文件类型 if (FileMagic.valueOf(inputStream) == FileMagic.OLE2 || FileMagic.valueOf(inputStream) == FileMagic.OOXML) { inputStream.reset(); // 重置流到标记位置 // 然后使用POI的WorkbookFactory创建Workbook Workbook workbook = WorkbookFactory.create(inputStream); // 处理workbook } else { throw new IllegalArgumentException("该文件不是有效的Excel文件(OLE2OOXML格式)"); } } ``` 注意:WorkbookFactory.create方法会再次读取流,所以我们在检测后重置流到开始位置。 如果不想依赖POI的FileMagic,也可以手动检查文件头: ```java public boolean isExpectedExcelHeader(InputStream inputStream) throws IOException { byte[] header = new byte[8]; int read = inputStream.read(header, 0, 8); if (read < 8) { return false; } // OLE2 header: D0 CF 11 E0 A1 B1 1A E1 byte[] ole2Header = {(byte)0xD0, (byte)0xCF, (byte)0x11, (byte)0xE0, (byte)0xA1, (byte)0xB1, (byte)0x1A, (byte)0xE1}; // OOXML header (zip): 50 4B 03 04,实际上我们只需要前4个字节,但为了统一读取8个,我们检查前4个 byte[] ooxmlHeader = {0x50, 0x4B, 0x03, 0x04}; // 检查OLE2 boolean isOle2 = true; for (int i=0; i<ole2Header.length; i++) { if (header[i] != ole2Header[i]) { isOle2 = false; break; } } // 检查OOXML(前4个字节) boolean isOoxml = true; for (int i=0; i<ooxmlHeader.length; i++) { if (header[i] != ooxmlHeader[i]) { isOoxml = false; break; } } return isOle2 || isOoxml; } ``` 同样,在使用这个方法后,我们需要重置流(如果后续还要使用这个流的话)。并且,在调用这个方法之前,确保流支持reset,或者使用BufferedInputStream包装。 如果文件不是Excel格式,那么我们应该给出明确的错误信息,提示用户上传正确的文件。 另外,如果文件是CSV等文本格式,我们不应该使用Excel解析器,而应该使用CSV解析器。 总结解决步骤: 1. 确保我们读取的是正确的文件,并且文件没有损坏。 2. 在代码中,使用支持mark/reset的流(如BufferedInputStream)来包装用户提供的InputStream。 3. 读取前先标记,然后读取足够的数据来检查文件头。 4. 根据文件头判断是否是Excel文件(OLE2OOXML)。 5. 如果是,重置流,然后使用POI的WorkbookFactory来解析。 6. 如果不是,则抛出异常,提示用户上传正确的Excel文件。 如果用户上传的是CSV文件,但错误地使用了Excel解析器,也会导致这个错误。因此,我们需要根据实际需求,如果支持CSV,则应该另外判断CSV格式(例如通过文件扩展名或内容分析)。 但是,错误信息明确提到“InputStream 不是 OLE2OOXML 格式”,所以当前程序期望的是Excel文件。因此,我们主要确保上传的是Excel文件。 最后,如果以上步骤都正确,但问题仍然存在,可能是文件损坏,可以尝试用Excel修复文件,或者让用户重新生成文件。 注意:在读取文件时,也要注意关闭流,使用try-with-resources来确保流被正确关闭。 示例代码(完整处理): ```java try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { is.mark(1024); if (FileMagic.valueOf(is) == FileMagic.OLE2 || FileMagic.valueOf(is) == FileMagic.OOXML) { is.reset(); Workbook workbook = WorkbookFactory.create(is); // 处理workbook } else { // 不是Excel文件 throw new InvalidFormatException("上传的文件不是有效的Excel格式"); } } catch (IOException e) { // 处理异常 } ``` 如果使用手动检查文件头的方式: ```java try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { is.mark(8); // 只需要8个字节 byte[] header = new byte[8]; int read = is.read(header); if (read < 8) { throw new InvalidFormatException("文件太小,无法确定格式"); } is.reset(); // 重置流 // 调用上面写的isExpectedExcelHeader方法,但这里我们直接写判断逻辑 if (isExcelHeader(header)) { Workbook workbook = WorkbookFactory.create(is); // 处理workbook } else { throw new InvalidFormatException("上传的文件不是有效的Excel格式"); } } ``` 其中,isExcelHeader方法就是上面我们写的方法。 希望这些步骤能帮助解决问题。</think>### 解决 "InputStream 不是 OLE2OOXML 格式" 错误 该错误通常发生在尝试用 Excel 解析器读取非 Excel 文件时。以下是系统化的解决方案: #### 1. **验证文件实际格式** ```java // 检查文件头前8字节 byte[] header = new byte[8]; try (InputStream is = new FileInputStream("yourfile")) { int read = is.read(header); String hexHeader = bytesToHex(header); // 转换为十六进制 System.out.println("文件头: " + hexHeader); } // 常见格式签名: // OLE2 (.xls): D0 CF 11 E0 A1 B1 1A E1 // OOXML (.xlsx): 50 4B 03 04 (PK头) // CSV/TXT:无固定签名 ``` #### 2. **使用正确的解析器** | 文件实际类型 | 正确解析方式 | 错误解析方式 | |--------------|----------------------------------|----------------------| | CSV/TXT | `BufferedReader` + 文本处理 | POI Excel 解析器 | | 纯文本 | `InputStreamReader` | `XSSFWorkbook` | | 损坏的 Excel | 文件修复工具 | 直接读取 | | 图片/PDF | 专用解析库 (如 PDFBox) | Excel 解析器 | #### 3. **修复代码实现** ```java try (InputStream is = Files.newInputStream(Paths.get("file"))) { // 先检测文件类型 if (isExcelFile(is)) { // 自定义检测函数 Workbook workbook = WorkbookFactory.create(is); // 自动识别格式 } else { // 回退到文本处理 BufferedReader reader = new BufferedReader( new InputStreamReader(is, StandardCharsets.UTF_8) ); reader.lines().forEach(System.out::println); } } catch (InvalidFormatException e) { // 明确处理格式错误 System.err.println("非Excel文件: " + e.getMessage()); } ``` #### 4. **关键排查点** 1. **文件扩展名验证** ```java String fileName = "report.csv.xlsx"; // 实际可能是CSV文件 String realType = Files.probeContentType(Paths.get(fileName)); ``` - 使用 `java.nio.file.Files.probeContentType()` 检测真实 MIME 类型 2. **流重置机制** ```java if (!inputStream.markSupported()) { inputStream = new BufferedInputStream(inputStream); } inputStream.mark(1024); // 标记位置 // ...检测操作... inputStream.reset(); // 重置流位置 ``` 3. **错误处理优化** ```java catch (InvalidFormatException ex) { throw new CustomException("请检查文件格式: " + ex.getLocalizedMessage() + " 支持格式: .xls, .xlsx, .csv"); } ``` #### 5. 常见文件格式签名参考[^1] | 格式 | 文件头 (HEX) | ASCII 等效 | |-------|----------------------|-------------| | Excel | `D0 CF 11 E0` | ÐÏà | | XLSX | `50 4B 03 04` | PK.. | | CSV | 无固定头(通常UTF-8 BOM: `EF BB BF`) | |
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值