SpringBoot+EasyPOI百万级数据导出Excel实战|分页防OOM+(含源码示例)
🛠️ 工具简介
本文介绍一款基于 EasyPOI 和 Apache POI SXSSF 的批量Excel导出工具类 BatchExcelExporter
,支持大数据量分页处理,避免内存溢出(OOM),适合百万级数据导出场景。
📌 环境准备
1. 引入Maven依赖
在 pom.xml
中添加以下依赖(确保使用最新版本):
<!-- EasyPOI 核心库 -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<!-- Apache POI (SXSSF 需要) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
2. 引入工具类
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class BatchExcelExporter<T> {
private final SXSSFWorkbook workbook;
private final Class<T> clazz;
private final List<ExcelExportEntity> exportEntities = new ArrayList<>();
private int currentRowIndex = 0;
public BatchExcelExporter(Class<T> clazz) {
this.clazz = clazz;
this.workbook = new SXSSFWorkbook(100); // 缓存100行到磁盘
initHeader();
}
// 初始化表头(仅执行一次)
private void initHeader() {
// 解析实体类注解
List<Field> fields = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> f.isAnnotationPresent(Excel.class))
.sorted(Comparator.comparing(f -> {
Excel excel = f.getAnnotation(Excel.class);
return excel.orderNum();
})).collect(Collectors.toList());
// 构建导出结构
fields.forEach(f -> {
Excel excel = f.getAnnotation(Excel.class);
exportEntities.add(new ExcelExportEntity(excel.name(), f.getName(), 4000));
});
// 创建表头
workbook.createSheet("数据").createRow(currentRowIndex++);
addHeaderRow(workbook.getSheetAt(0).getRow(0));
}
// 添加表头行
private void addHeaderRow(org.apache.poi.ss.usermodel.Row headerRow) {
for (int i = 0; i < exportEntities.size(); i++) {
headerRow.createCell(i).setCellValue(exportEntities.get(i).getName());
}
}
// 批量添加数据(分页调用)
public void addBatchData(List<T> batchData) throws Exception {
org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
for (T data : batchData) {
org.apache.poi.ss.usermodel.Row row = sheet.createRow(currentRowIndex++);
fillDataRow(row, data);
}
// 每500行刷新到磁盘(根据数据量调整)
if (currentRowIndex % 500 == 0) {
((SXSSFSheet) sheet).flushRows(500);
}
}
// 填充数据行(利用反射动态取值)
private void fillDataRow(org.apache.poi.ss.usermodel.Row row, T data) throws Exception {
for (int i = 0; i < exportEntities.size(); i++) {
Field field = clazz.getDeclaredField((String) exportEntities.get(i).getKey());
field.setAccessible(true);
Object value = field.get(data);
row.createCell(i).setCellValue(value != null ? value.toString() : "");
}
}
// 最终写入文件
public void exportToFile(String filePath) throws Exception {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
workbook.write(fos);
} finally {
workbook.dispose(); // 清理临时文件
}
}
}
🚀 工具类核心功能
- 动态表头生成:通过解析实体类的
@Excel
注解自动生成表头。 - 分页批量写入:支持分批次添加数据,减少内存占用。
- 高性能处理:基于
SXSSFWorkbook
,默认缓存100行到磁盘。
📝 使用示例
1. 定义实体类
public class User {
@Excel(name = "姓名", orderNum = "0")
private String name;
@Excel(name = "年龄", orderNum = "1")
private Integer age;
@Excel(name = "邮箱", orderNum = "2")
private String email;
// 省略Getter/Setter
}
2. 调用工具类导出数据
public class ExcelExportDemo {
public static void main(String[] args) throws Exception {
// 初始化导出器(指定实体类)
BatchExcelExporter<User> exporter = new BatchExcelExporter<>(User.class);
// 模拟分页查询数据(假设每次查询1000条)
for (int page = 1; page <= 10; page++) {
List<User> batchData = queryUsersByPage(page, 1000);
exporter.addBatchData(batchData);
}
// 导出到文件
exporter.exportToFile("user_export.xlsx");
}
private static List<User> queryUsersByPage(int page, int size) {
// 模拟数据库查询(此处返回测试数据)
List<User> users = new ArrayList<>();
for (int i = 0; i < size; i++) {
users.add(new User("User" + i, 20 + i, "user" + i + "@example.com"));
}
return users;
}
}
🧠 关键代码解析
1. 表头自动生成
通过反射解析实体类的 @Excel
注解,按 orderNum
排序后生成表头:
List<Field> fields = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> f.isAnnotationPresent(Excel.class))
.sorted(Comparator.comparing(f -> f.getAnnotation(Excel.class).orderNum()))
.collect(Collectors.toList());
2. 数据分页写入
批量添加数据时,每500行刷新到磁盘(避免内存堆积):
public void addBatchData(List<T> batchData) throws Exception {
// ... 填充数据 ...
if (currentRowIndex % 500 == 0) {
((SXSSFSheet) sheet).flushRows(500); // 🚨 关键性能优化点!
}
}
⚠️ 注意事项
- 字段类型匹配:确保实体类字段类型与Excel数据兼容(如日期格式需特殊处理)。
- 性能调优:根据服务器内存调整
SXSSFWorkbook
的缓存行数(默认100)。 - 异常处理:建议添加
try-catch
块捕获反射和IO异常。
🌟 效果展示
导出后的Excel文件示例:
姓名 | 年龄 | 邮箱 |
---|---|---|
User0 | 20 | user0@example.com |
User1 | 21 | user1@example.com |
🔗 扩展优化方向
- 样式自定义:通过
SXSSFCellStyle
设置单元格样式(如背景色、边框)。 - 多Sheet支持:扩展工具类以支持多Sheet动态切换。
- 异步导出:结合线程池实现异步导出任务。
源码获取:文中的 BatchExcelExporter
完整代码已提供,可直接复制使用!
💬 互动话题:你在Excel导出中遇到过哪些坑?欢迎评论区交流!