[alibaba/easyexcel]填充Excel 会导致内存溢出

2024-05-10 778 views
0

我在实际项目中写的代码

方法:exportHeader public ServletOutputStream exportHeader(HttpServletResponse response, String fileName) throws IOException { //设置字符集为utf-8 // response.reset(); response.setContentType(CONTENT_TYPE); response.setHeader(ACCESS_CONTROL_EXPOSE, CONTENT_DISPOSITION); //通知浏览器服务器发送的数据格式 response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(fileName, CHARACTER)); //发送一个报头,告诉浏览器当前页面不进行缓存,每次访问的时间必须从服务器上读取最新的数据 return response.getOutputStream(); }

实现数据填充: ServletOutputStream outputStream = exportHeader(response, fileName);

ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build(); WriteSheet writeSheet = EasyExcel.writerSheet().sheetNo(1).build(); excelWriter.fill(this::dynamicFillAddValues, writeSheet); //查询数据填充到 sheet

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

出现的问题: 每点击一次下载内存会增加上百兆,随着点击次数的增加,服务器内存溢出。fill 动态填充会导致内存溢出该如何解决?

回答

6

// 方案3 分多次 填充 会使用文件缓存(省内存) fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(data(), writeSheet); excelWriter.fill(data(), writeSheet); // 千万别忘记关闭流 excelWriter.finish();

我参考的是这个例子,输入输出流是按项目实际开发场景替换的。 fill 数据填充导致内存溢出的BUG 请社区引起重视。

5

看看有没有其他报错之类的? sheetno改成0试试。然后你的 inputStream是什么 ,多大? 还有尝试删除easyexcel代码,是否还存在问题?

9

利用重复多次填充 // 方法1 如果写到同一个sheet String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; ExcelWriter excelWriter = null; try { // 这里 需要指定写用哪个class去写 excelWriter = EasyExcel.write(fileName, DemoData.class).build(); // 这里注意 如果同一个sheet只要创建一次 WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来 for (int i = 0; i < 5; i++) { // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 List<DemoData> data = data(); excelWriter.write(data, writeSheet); } } finally { // 千万别忘记finish 会帮忙关闭流 if (excelWriter != null) { excelWriter.finish(); } }

4

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

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

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

技术: springBoot

控制层:

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

服务实现层:

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;    

}

0

完整的实现过程就是这样,没有报错,可以下载模板。 消耗最大在 ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build();

2

请问怎么解决的

8

excelWriter.fill(dynamicFillAddValues(), writeSheet); 这种写法会把excel 模板整个加入内存中所以耗费资源 你可以用 write() 这个方法写入会省很多内存