org.apache.poi.xssf.usermodel.XSSFWorkbook写入文件与ByteArrayOutputStream结果不一致致邮件附件损坏问题求助
解决POI Workbook写入ByteArrayOutputStream后附件损坏的问题
你遇到的这个问题其实是Apache POI的Workbook对象的一个常见坑——同一个Workbook实例只能调用一次write方法,第二次调用的时候,内部的数据流已经被耗尽或者资源被关闭了,所以写出的内容自然是不完整的,这就是为什么你的brad1.xlsx和正常的brad.xlsx差了几百字节,而且打开会损坏。
问题根源
以XSSFWorkbook(对应.xlsx格式)为例,它本质是基于ZIP压缩包的结构。当你第一次调用wb.write(fileOut)时,POI会完成整个ZIP包的归档操作,包括关闭内部的压缩流、写入ZIP的结束标记等。这时候Workbook对象的内部状态已经被“消费”了,第二次调用wb.write(bos)时,它无法再生成完整的ZIP内容,只能写出部分残留数据,导致文件损坏。
解决方案
核心思路是:只调用一次Workbook的write方法,将内容写入ByteArrayOutputStream作为中间载体,再从这个载体复制到多个输出目标(比如磁盘文件和邮件附件)。修改后的代码如下:
ByteArrayOutputStream bos = new ByteArrayOutputStream(); Workbook wb = (Workbook) IDataUtil.get(pipelineCursor, "MSWorkbook"); // 关键:只调用一次write,将Workbook内容写入ByteArrayOutputStream try { wb.write(bos); bos.flush(); // 确保所有数据都写入bos } catch (IOException e) { e.printStackTrace(); } // 从bos写入磁盘文件(如果需要保存到本地) try (FileOutputStream fileOut = new FileOutputStream("/tmp/brad.xlsx")) { bos.writeTo(fileOut); // 复用bos的内容,不需要再次调用wb.write } catch (IOException e) { e.printStackTrace(); } // 将bos的内容转为字节数组,用于邮件附件 byte[] attachmentBytes = bos.toByteArray(); // -------------------------- // 这里添加邮件发送的代码,示例: // MimeBodyPart attachmentPart = new MimeBodyPart(); // // 指定正确的MIME类型,xlsx对应下面的类型,xls则用"application/vnd.ms-excel" // attachmentPart.setDataHandler(new DataHandler(new ByteArrayDataSource(attachmentBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))); // attachmentPart.setFileName("your-file.xlsx"); // // 将附件添加到邮件中... // -------------------------- // 最后关闭资源 try { bos.close(); wb.close(); // 别忘了关闭Workbook本身,释放资源 } catch (IOException e) { e.printStackTrace(); }
额外注意事项
- 如果你需要多次使用Workbook的内容,除了用ByteArrayOutputStream中转,也可以在第一次write之前,通过
WorkbookFactory重新读取原始的输入流(如果有)来创建新的Workbook实例,但这种方式效率较低,不如用ByteArrayOutputStream中转高效。 - 无论使用哪种Workbook实现(XSSF或HSSF),都应该遵循“一次write”的原则,避免重复调用write方法导致的异常或损坏。
内容的提问来源于stack exchange,提问作者Bradley Biggers
相关产品推荐
相关产品推荐

