杰瑞科技汇

Java如何高效导出大数据量Excel?

Java导出大数据量Excel方案

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

Java如何高效导出大数据量Excel?-图1
(图片来源网络,侵删)

使用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);

性能优化建议

  1. 分批处理:每次查询和写入一定数量的数据(如10000行)
  2. 关闭自动列宽sheet.setColumnWidth(columnIndex, width) 在写入完成后设置
  3. 使用流式API:避免一次性加载所有数据到内存
  4. 调整JVM参数:增加堆内存 -Xms -Xmx
  5. 使用压缩:对于网络传输,可以考虑先压缩再下载
  6. 监控内存:添加内存监控,及时发现问题

注意事项

  • 对于千万级数据,导出时间可能较长,建议采用异步导出
  • 导出过程中避免进行复杂计算或业务逻辑处理
  • 考虑添加导出进度查询功能
  • 对于Web应用,注意导出完成后清理临时文件

方案可根据实际数据量和业务需求选择合适的实现方式。

Java如何高效导出大数据量Excel?-图2
(图片来源网络,侵删)
Java如何高效导出大数据量Excel?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇