Java导出大数据量Excel方案
导出大数据量Excel时,传统的POI或EasyExcel方式可能会导致内存溢出(OutOfMemoryError)或性能问题,以下是几种高效的大数据量Excel导出方案:

使用EasyExcel的SXSSFWorkbook模式
EasyExcel基于Apache POI的SXSSFWorkbook,专门针对大数据量优化:
// 使用SXSSFWorkbook,设置行访问窗口大小
SXSSFWorkbook workbook = new SXSSFWorkbook(1000); // 在内存中保留1000行数据
Sheet sheet = workbook.createSheet("数据");
// 写入表头
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("ID");
headerRow.createCell(1).setCellValue("名称");
// 分批写入数据
for (int i = 0; i < 1000000; i++) {
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(i);
row.createCell(1).setCellValue("名称" + i);
// 每1000行刷新一次到磁盘
if (i % 1000 == 0) {
((SXSSFSheet) sheet).flushRows();
}
}
// 输出到文件
try (FileOutputStream out = new FileOutputStream("large_data.xlsx")) {
workbook.write(out);
} finally {
workbook.dispose(); // 清理临时文件
}
使用CSV格式(推荐超大数据量)
对于超大数据量(千万级以上),CSV格式是最佳选择:
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("large_data.csv"))) {
// 写入表头
writer.write("ID,名称\n");
// 分批写入数据
for (int i = 0; i < 10000000; i++) {
writer.write(String.format("%d,名称%d\n", i, i));
// 可选:每10000行刷新一次
if (i % 10000 == 0) {
writer.flush();
}
}
}
使用异步导出+分片查询
结合数据库分页查询和异步导出:
// 1. 控制层接收导出请求
@GetMapping("/export")
public void exportExcel(HttpServletResponse response) throws IOException {
// 设置响应头
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("数据导出", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 异步导出
CompletableFuture.runAsync(() -> {
try {
exportService.exportLargeData(response.getOutputStream());
} catch (IOException e) {
log.error("导出失败", e);
}
});
}
// 2. 服务层实现
public void exportLargeData(OutputStream outputStream) {
ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
WriteSheet writeSheet = EasyExcel.writerSheet("数据").build();
int pageSize = 10000;
int currentPage = 1;
while (true) {
// 分页查询数据
List<DataEntity> dataList = dataMapper.selectPage(currentPage, pageSize);
if (CollectionUtils.isEmpty(dataList)) {
break;
}
// 写入Excel
excelWriter.write(dataList, writeSheet);
currentPage++;
}
// 关闭资源
excelWriter.finish();
}
使用模板导出(适用于固定格式)
// 1. 准备模板文件(template.xlsx) // 2. 使用模板导出 String templateFileName = "template.xlsx"; String fileName = "large_data_template.xlsx"; // 填充数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); // 假设dataList是分批查询的数据 excelWriter.fill(dataList, fillConfig, writeSheet);
性能优化建议
- 分批处理:每次查询和写入一定数量的数据(如10000行)
- 关闭自动列宽:
sheet.setColumnWidth(columnIndex, width)在写入完成后设置 - 使用流式API:避免一次性加载所有数据到内存
- 调整JVM参数:增加堆内存
-Xms -Xmx - 使用压缩:对于网络传输,可以考虑先压缩再下载
- 监控内存:添加内存监控,及时发现问题
注意事项
- 对于千万级数据,导出时间可能较长,建议采用异步导出
- 导出过程中避免进行复杂计算或业务逻辑处理
- 考虑添加导出进度查询功能
- 对于Web应用,注意导出完成后清理临时文件
方案可根据实际数据量和业务需求选择合适的实现方式。


