HSSFWorkbook是操作Excel2003以前(包括2003)的版本,扩展名为.xls,所以每个Sheet局限就是导出的行数至多为65535行,一般不会发生内存不足的情况(OOM)。
这种形式的出现是由于HSSFWorkbook的局限性而产生的,因为其所导出的行数比较少,并且只针对Excel2003以前(包括2003)的版本的版本,所以 XSSFWookbook应运而生,其对应的是EXCEL2007以后的版本(1048576行,16384列)扩展名.xlsx,每个Sheet最多可以导出104万行,不过这样就伴随着一个OOM内存溢出的问题,原因是你所创建的sheet row cell 等此时是存在内存中的,随着数据量增大 ,内存的需求量也就增大,那么很大可能就是要OOM了。
SXSSF通过一个滑动窗口来限制访问Row的数量从而达到低内存占用的目录,XSSF可以访问所有行。旧的行数据不再出现在滑动窗口中并变得无法访问,与此同时写到磁盘上。
在自动刷新的模式下,可以指定窗口中访问Row的数量,从而在内存中保持一定数量的Row。当达到这一数量时,在窗口中产生新的Row数据,并将低索引的数据从窗口中移动到磁盘中。每个Sheet最多可以导出104万行,
方案
- 大数据导出时会导致nginx响应超时
- 前端触发导出,后端可以直接先返回前端正在导出中,后端慢慢执行导出
- 导出数据时可以开启多线程分批查询然后分批分sheet写入Excel(CompletableFuture多线程异步计算)
- 等所有线程执行完毕Excel内容全部写入完成后临时保存文件到本地项目
- 前端开启一个定时轮训调另外一个读文件接口
- 如果导出后是保存到本地,后端提供一个文件流读取导出文件的接口,待Excel写入完成后读取文件返回前端
- Excel写入完成后也可以上传到文件服务器,上传完成后把文件路径保存到redis,然后前端轮询调后端查路径的接口,直接查询到路径返回前端直接点击下载
- 注意事项:sheet要先创建,开启多线程创建的时候会报错,sheet要按顺序创建,根据数据量创建sheet数量,生成文件时会先生成一个空白文件,然后再导入内容,这个需要一段时间
public Object exportTest() {
OutputStream os = null;
try {
long start = System.currentTimeMillis();
SXSSFWorkbook workbook = new SXSSFWorkbook();
//1.无返回值的异步任务 runAsync()
SXSSFSheet sheet1 = workbook.createSheet("1");
SXSSFSheet sheet2 = workbook.createSheet("2");
SXSSFSheet sheet3 = workbook.createSheet("3");
SXSSFSheet sheet4 = workbook.createSheet("4");
SXSSFSheet sheet5 = workbook.createSheet("5");
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
for (int i = 0; i < 1000000; i++) {
SXSSFRow row = sheet1.createRow(i);
for (int j = 0; j < 10; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue("导出测试" + j);
}
}
});
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
for (int i = 0; i < 1000000; i++) {
SXSSFRow row = sheet2.createRow(i);
for (int j = 0; j < 10; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue("导出测试" + j);
}
}
});
CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
for (int i = 0; i < 1000000; i++) {
SXSSFRow row = sheet3.createRow(i);
for (int j = 0; j < 10; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue("导出测试" + j);
}
}
});
CompletableFuture<Void> future4 = CompletableFuture.runAsync(() -> {
for (int i = 0; i < 1000000; i++) {
SXSSFRow row = sheet4.createRow(i);
for (int j = 0; j < 10; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue("导出测试" + j);
}
}
});
CompletableFuture<Void> future5 = CompletableFuture.runAsync(() -> {
for (int i = 0; i < 1000000; i++) {
SXSSFRow row = sheet5.createRow(i);
for (int j = 0; j < 10; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue("导出测试" + j);
}
}
});
CompletableFuture<Void> future = CompletableFuture.allOf(future1,future2,future3,future4,future5);
future.join();
String rootPath = this.getClass().getResource("/").getPath();
String filePath = rootPath + "template//导出测试.xlsx";
File testFile = new File(filePath);
File fileParent = testFile.getParentFile();//返回的是File类型,可以调用exsit()等方法
String fileParentPath = testFile.getParent();//返回的是String类型
System.out.println("fileParent:" + fileParent);
System.out.println("fileParentPath:" + fileParentPath);
if (!fileParent.exists()) {
fileParent.mkdirs();// 能创建多级目录
}
if (!testFile.exists()) {
testFile.createNewFile();//有路径才能创建文件
}
os = new FileOutputStream(testFile);
long end = System.currentTimeMillis();
System.out.println("耗时======" + (end - start) / 1000);
workbook.write(os);
os.flush();
os.close();
long end1 = System.currentTimeMillis();
System.out.println("耗时======" + (end1- start) / 1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.flush();
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "导出成功";
}

被折叠的 条评论
为什么被折叠?



