使用Apache POI导出Excel时出现“Stream closed”异常求助
问题分析与修复方案
嘿,我帮你看了下代码和抛出的异常,问题其实出在你用try-with-resources处理ServletOutputStream的方式上。
先看你遇到的异常:
Exceptin in excel Stream is closed [err] java.io.IOException: Stream is closed [err] at com.ibm.ws.http.channel.internal.outbound.HttpOutputStreamImpl.validate(HttpOutputStreamImpl.java:213) [err] at [internal classes]
你的核心代码片段如下:
public static void prepareDateForCSVGeneration(List<DataList> dataList,HttpServletResponse response) throws CashBookingInquiryServiceApplicationException { try (ServletOutputStream out = response.getOutputStream()) { XSSFWorkbook workbook = new XSSFWorkbook(); //below method creates headers and rows convertToExcel(workbook, dataList); response.setContentType("text/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=test.xlsx"); workbook.write(out); out.flush(); } catch (IOException exception) { System.out.println("Exceptin in excel "+exception.getMessage()); exception.printStackTrace() ; } }
问题根源
try-with-resources的特性是在代码块执行完毕后自动关闭所有实现了AutoCloseable接口的资源,这里的ServletOutputStream确实实现了该接口,所以try块结束后它会被自动关闭。但这个流是由Web容器(比如你用的IBM WebSphere)创建和管理的,容器在请求处理流程结束后,可能还会尝试对这个流做收尾操作,而你提前关闭了它,就触发了流已关闭的IO异常。
修复方案
我们需要调整资源管理的方式:
- 不要用try-with-resources管理
ServletOutputStream,把它的生命周期交给容器控制,不要手动(包括自动)关闭。 - 手动管理你自己创建的
XSSFWorkbook资源,确保它被正确关闭,避免内存泄漏。
修改后的代码如下:
public static void prepareDateForCSVGeneration(List<DataList> dataList, HttpServletResponse response) throws CashBookingInquiryServiceApplicationException { ServletOutputStream out = null; XSSFWorkbook workbook = null; try { out = response.getOutputStream(); workbook = new XSSFWorkbook(); convertToExcel(workbook, dataList); // 修正xlsx对应的MIME类型,比text/vnd.ms-excel更规范 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=test.xlsx"); workbook.write(out); out.flush(); } catch (IOException exception) { System.out.println("Exception in excel " + exception.getMessage()); exception.printStackTrace(); } finally { // 关闭我们自己创建的workbook资源 if (workbook != null) { try { workbook.close(); } catch (IOException e) { e.printStackTrace(); } } // 不要关闭out,交给容器处理 } }
额外小建议
- 你原来的catch块里拼写了
Exceptin,建议改成Exception,让日志更规范。 - 原来设置的
text/vnd.ms-excel是xls格式的MIME类型,xlsx格式用application/vnd.openxmlformats-officedocument.spreadsheetml.sheet更符合标准,虽然不影响下载,但推荐修正。
内容的提问来源于stack exchange,提问作者MiniSu




