[alibaba/easyexcel]填充Excel 遇到OOM

2024-05-10 247 views
8

你好,我把实现流程完整描述一下。

背景: 我们项目要实现一个 模板下载,模板里面有个sheet(不是第一个sheet)的数据是动态查询填充上去的。模板文件存储在OSS, OSS 会返回路径,例如:http://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/wos/workOrder/issueFile1。 该 OSS 路径可以下载附件。

问题: 附件大小在 100-200KB 左右,每下载一次,JVM 内存飙升上百MB,次数多了,造成OOM

技术: springBoot

控制层:FileController

PostMapping(value = "/download") public void downloadFile(HttpServletResponse response, String ossUrl) throws Exception { fileService.downloadFile(response, param); }

服务实现层:FileServiceImpl

public void downloadFile(HttpServletResponse response, String ossUrl) throws IOException { URL url = new URL(ossUrl); URLConnection conn = url.openConnection(); conn.setConnectTimeout(3 * 1000); // 设置超时间为3秒 conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36"); InputStream inputStream = conn.getInputStream();

String fileName = "数据填充模板.xlsx";
//数据填充
issueDynamicFill(response, fileName, inputStream);

}

public void issueDynamicFill(HttpServletResponse response,
InputStream inputStream) throws IOException { ServletOutputStream outputStream = exportHeader(response, fileName);

ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build();
WriteSheet writeSheet = EasyExcel.writerSheet().sheetNo(2).build();

//dynamicFillAddValues 原本是查库的,为省略时间,数据写死
excelWriter.fill(dynamicFillAddValues(), writeSheet);

excelWriter.finish();
inputStream.close();
outputStream.flush();

}

public ServletOutputStream exportHeader(HttpServletResponse response, String fileName) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, CHARACTER)); return response.getOutputStream(); }

public List dynamicFillAddValues(){ return initData(); }

//初始化数据 public List initData(){ String jsonStr = "[\n" + " {\n" + " \"addValueCode\":\"201\",\n" + " \"addValueName\":\"换单\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"203\",\n" + " \"addValueName\":\"更换包装\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"206\",\n" + " \"addValueName\":\"加固\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"207\",\n" + " \"addValueName\":\"核重\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"205\",\n" + " \"addValueName\":\"分箱\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"204\",\n" + " \"addValueName\":\"合箱\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"202\",\n" + " \"addValueName\":\"清点拍照\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"208\",\n" + " \"addValueName\":\"核重拍照\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"209\",\n" + " \"addValueName\":\"核材积\"\n" + " },\n" + " {\n" + " \"addValueCode\":\"210\",\n" + " \"addValueName\":\"核材积拍照\"\n" + " }\n" + "]"; return JSONObject.parseArray(jsonStr, AddValueService.class);
}

实体:

@Data @Builder @AllArgsConstructor @NoArgsConstructor @ApiModel(value = "AddValueService") public class AddValueService {

@ApiModelProperty(value = "编号")
private String addValueCode;

@ApiModelProperty(value = "名称")
private String addValueName;    

}

回答

9

在构建 ExcelWriter 时,占用内存特别大,100 - 200 MB/每次 ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build();

4

Files will be generated in memory when using OutputStream. Which may result in OOM if many files are generated at the same time.You can generate files in the temp folder and download them in OutputStream.

8

模板需要小,建议搞个额外存储的地方,然后参照多次写入。 在模板里面一直追加数据每次都要重新打开所有数据,非常容易OOM.