SXSSFWorkbook写入数据时提示document已关闭异常该如何排查
异常触发原因
你的代码执行顺序存在逻辑错误:
- 你将
workbook::write方法引用绑定给StreamingOutput时,仅做了方法绑定,并未真正执行写入操作,实际写入逻辑是由Jersey框架在你返回Response对象之后才异步触发的。 - 你在构造完Response对象、返回结果之前,就提前执行了
workbook.dispose()和workbook.close(),这两个操作会释放SXSSFWorkbook持有的OPCPackage资源,将对应引用置为null。 - 当Jersey框架后续触发响应流写入,调用
workbook.write方法时,就会命中你贴出的源码中p == null的判断,抛出对应异常。
你用XSSFWorkbook未复现问题,是因为XSSF的普通写入逻辑是同步执行,你之前的XSSF代码大概率是在write执行完成后才关闭资源,和当前异步流处理的执行时序不同。
修复方案
核心原则是必须等workbook的write操作执行完成后,再释放资源,禁止提前关闭workbook,修改后的代码参考:
public static Response createResponseUsingStreaming() throws IOException { SXSSFWorkbook workbook = report.generateStreamingExcelReport(100); final String contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; // 重写StreamingOutput的写入逻辑,在写入完成后再释放资源 StreamingOutput outputStream = output -> { try { workbook.write(output); } finally { workbook.dispose(); workbook.close(); } }; Response.ResponseBuilder responseBuilder = Response.ok(outputStream); // 补充xlsx后缀,避免浏览器识别格式错误 responseBuilder.header("Content-Disposition", "attachment; filename=test.xlsx"); responseBuilder.header("Access-Control-Expose-Headers", "Content-Disposition"); responseBuilder.header("Content-Type", contentType); return responseBuilder.build(); }
额外提示:你之前填写的application/vnd.ms-excel是xls格式的MIME类型,SXSSF生成的是xlsx格式,建议替换为代码中的对应MIME类型,避免部分设备下载后无法正常打开文件。
内容的提问来源于stack exchange,提问作者Suhas Munishamaiah
相关产品推荐
相关产品推荐

