异常日志
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:582) ~[tomcat-embed-core-9.0.17.jar!/:9.0.17]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:227) ~[tomcat-embed-core-9.0.17.jar!/:9.0.17]
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:152) ~[javax.servlet-api-3.1.0.jar!/:3.1.0]
源码位置
看起来是,output流使用标志生效导致异常。
异常原因
一句话总结:同一个请求中:调用了两次response.getOutputStream()方法。或者先进行了respose的头等信息设置后调用response.getOutputStream()
异常前代码
控制层:
@RequestMapping(value ="/detailExport" ,method = RequestMethod.POST)
@BusiResponseBody
public void detailExport(@RequestBody IdReqBO queryBO, HttpServletRequest request,HttpServletResponse response ) {
//获取导出的元数据(转换器、格式控制器、导出内容等)
ExcelExportContent excelExportContent = excelExportStrategy.exportDetailExcelContent(request.getRequestURI(), queryBO);
EasyExcelUtils.ExportResponse(excelExportContent,response);
}
工具类:
public class EasyExcelUtils {
public static void ExportResponse(ExcelExportContent excelExportContent,HttpServletResponse response){
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(excelExportContent.getFileName(), "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//这里先设置的response的头、编码等信息,导致response.getOutputStream()时异常
ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream(), excelExportContent.getAccountDataClass())
.registerConverter(new BigDecimalStringConverter())
.registerConverter(new DateStringConverter());
List<WriteHandler> writeHandler = excelExportContent.getWriteHandler();
for (WriteHandler handler : writeHandler) {
excelWriterBuilder.registerWriteHandler(handler);
}
excelWriterBuilder
.sheet(excelExportContent.getSheetName())
.doWrite(excelExportContent.getAccountData());
} catch (Exception e){
e.printStackTrace();
throw new ZTBusinessException(e.getMessage());
}
}
}
调整后代码
控制层不变:
@RequestMapping(value ="/detailExport" ,method = RequestMethod.POST)
@BusiResponseBody
public void detailExport(@RequestBody IdReqBO queryBO, HttpServletRequest request,HttpServletResponse response ) {
//获取导出的元数据(转换器、格式控制器、导出内容等),可忽略
ExcelExportContent excelExportContent = excelExportStrategy.exportDetailExcelContent(request.getRequestURI(), queryBO);
EasyExcelUtils.ExportResponse(excelExportContent,response);
}
工具类:
这里格式化response的动作只能在response.getOutputStream()之后,在EasyExcel的doWrite之前。
在response.getOutputStream()之前会出现getOutputStream() has already been called for this response异常。在EasyExcel的doWrite之后,流已经被写回了,格式化response不会生效,写回的内容是txt格式。
public class EasyExcelUtils {
protected static Logger logger = LoggerFactory.getLogger(EasyExcelUtils.class);
public static void ExportResponse(ExcelExportContent excelExportContent, HttpServletResponse response) {
try {
ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream());
//这里格式化response内容只能在这个位置
FormatResponse(response, excelExportContent.getFileName());
ExcelWriterSheetBuilder(excelWriterBuilder, excelExportContent).doWrite(excelExportContent.getDetailData());
} catch (Exception e) {
e.printStackTrace();
throw new ZTBusinessException("ExportResponse exception:" + e.getMessage());
}
}
public static void FormatResponse(HttpServletResponse response, String fileName) {
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String exportFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + exportFileName + ".xlsx");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new ZTBusinessException("FormatResponse exception:" + e.getMessage());
}
}
private static ExcelWriterSheetBuilder ExcelWriterSheetBuilder(ExcelWriterBuilder excelWriterBuilder, ExcelExportContent excelExportContent) {
List<Converter> converterList = Optional.ofNullable(excelExportContent.getConverter()).orElseGet(ArrayList::new);
for (Converter converter : converterList) {
excelWriterBuilder.registerConverter(converter);
}
List<WriteHandler> writeHandlerList = Optional.ofNullable(excelExportContent.getWriteHandler()).orElseGet(ArrayList::new);
for (WriteHandler writeHandler : writeHandlerList) {
excelWriterBuilder.registerWriteHandler(writeHandler);
}
ExcelWriterSheetBuilder excelWriterSheetBuilder = excelWriterBuilder.sheet(excelExportContent.getSheetName());
return excelWriterSheetBuilder;
}
}