以下是基于 Spring Boot 整合 EasyPoi 实现复杂多级表头 Excel 导出的完整方案,包含架构设计、核心代码和关键配置说明:
一、技术选型方案
css
graph LR
A[Spring Boot] --> B[Controller层]
B --> C[Service层]
C --> D[Entity实体类]
D --> E[EasyPoi导出引擎]
E --> F[Excel文件]
二、环境配置
- Maven 依赖
xml
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.4.0</version>
</dependency>
- Spring Boot 配置
ini
# Excel 导出配置
easypoi.export.head-rows=1
easypoi.export.sheet-name=Sheet1
三、核心实现
1. 多级表头实体类定义
less
@Data
public class FinancialReport {
// 一级表头:财务概览
@Excel(name = "财务概览/总收入", orderNum = "0", width = 20)
private BigDecimal totalIncome;
// 二级表头嵌套
@Excel(name = "部门明细", orderNum = "1")
private Department department;
// 集合类型表头
@ExcelCollection(name = "项目明细")
private List<Project> projects;
}
@Data
public class Department {
@Excel(name = "部门名称", orderNum = "0", width = 15)
private String deptName;
@Excel(name = "人员规模", orderNum = "1", width = 10)
private Integer staffCount;
}
@Data
public class Project {
@Excel(name = "项目名称", orderNum = "0", width = 25)
private String projectName;
// 三级表头
@Excel(name = "成本明细/人力成本", orderNum = "1", width = 15)
private BigDecimal laborCost;
@Excel(name = "成本明细/设备成本", orderNum = "2", width = 15)
private BigDecimal equipmentCost;
}
2. 导出工具类实现
scss
public class ExcelExportUtil {
public static Workbook createWorkbook(List<FinancialReport> data) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("财务报告");
// 创建表头行
Row headerRow = sheet.createRow(0);
// 动态构建多级表头
buildHeaderRow(headerRow, 0, data.getClass().getDeclaredFields());
// 填充数据
int rowNum = 1;
for (FinancialReport report : data) {
Row row = sheet.createRow(rowNum++);
fillDataRow(row, report);
}
// 自动调整列宽
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
sheet.autoSizeColumn(i);
}
return workbook;
}
private static void buildHeaderRow(Row row, int level, Field[] fields) {
for (Field field : fields) {
Excel excel = field.getAnnotation(Excel.class);
if (excel != null) {
Cell cell = row.createCell(excel.orderNum());
cell.setCellValue(excel.name());
// 处理合并单元格
if (excel.mergeVertical() || excel.mergeHorizontal()) {
sheet.addMergedRegion(new CellRangeAddress(
0, 0,
excel.orderNum(),
excel.orderNum() + excel.name().split("/").length - 1
));
}
// 递归处理嵌套表头
if (!Collection.class.isAssignableFrom(field.getType())) {
buildHeaderRow(row, level + 1, field.getType().getDeclaredFields());
}
}
}
}
private static void fillDataRow(Row row, Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Cell cell = row.createCell(field.getAnnotation(Excel.class).orderNum());
cell.setCellValue(field.get(obj).toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
3. Controller 层实现
less
@RestController
@RequestMapping("/export")
public class ExcelController {
@Autowired
private ReportService reportService;
@GetMapping("/financial-report")
public void exportFinancialReport(HttpServletResponse response) {
try {
// 生成数据
List<FinancialReport> data = reportService.generateReportData();
// 创建工作簿
Workbook workbook = ExcelExportUtil.createWorkbook(data);
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode("财务报告.xlsx", "UTF-8"));
// 写入输出流
workbook.write(response.getOutputStream());
workbook.close();
} catch (Exception e) {
throw new RuntimeException("导出失败:" + e.getMessage());
}
}
}
四、关键配置说明
1. 表头合并策略
注解属性 | 作用说明 | 示例值 |
---|---|---|
mergeVertical |
垂直合并单元格 | true |
mergeHorizontal |
水平合并单元格 | true |
orderNum |
列排序序号 | "0" |
width |
列宽(单位:像素) | 20 |
2. 性能优化配置
ini
# 大数据量导出配置
easypoi.export.big-data=true
easypoi.export.row-access-window-size=100
五、高级功能实现
1. 动态表头构建
ini
public class DynamicHeaderBuilder {
public static List<ExcelExportEntity> buildDynamicHeader() {
List<ExcelExportEntity> entities = new ArrayList<>();
// 一级表头
ExcelExportEntity parent = new ExcelExportEntity("财务分析", "finance");
parent.setNeedMerge(true);
// 二级表头
ExcelExportEntity child1 = new ExcelExportEntity("收入分析", "income");
ExcelExportEntity child2 = new ExcelExportEntity("支出分析", "expense");
// 三级表头
ExcelExportEntity grandChild1 = new ExcelExportEntity("销售收入", "sales");
ExcelExportEntity grandChild2 = new ExcelExportEntity("运营成本", "operating");
// 构建层级关系
child1.setList(Arrays.asList(grandChild1));
child2.setList(Arrays.asList(grandChild2));
parent.setList(Arrays.asList(child1, child2));
entities.add(parent);
return entities;
}
}
2. 自定义样式配置
scss
public class CustomStyleUtil {
public static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setColor(IndexedColors.WHITE.getIndex());
style.setFont(font);
style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setAlignment(HorizontalAlignment.CENTER);
return style;
}
}
六、常见问题解决方案
问题现象 | 解决方案 |
---|---|
表头层级错乱 | 检查@Excel.name 路径分隔符是否正确,确保/ 数量与层级一致 |
合并单元格失效 | 添加@Excel(mergeVertical = true) 注解或自定义合并策略 |
数据类型转换异常 | 使用@Excel(importFormat = "yyyy-MM-dd") 指定格式化规则 |
导出文件名乱码 | 使用URLEncoder.encode() 处理文件名编码 |
大数据量导出内存溢出 | 启用SXSSF模式:ExcelType.SXSSF 并设置窗口大小 |
七、扩展应用场景
- 模板导出
ini
TemplateExportParams params = new TemplateExportParams("template.xlsx");
Map<String, Object> data = new HashMap<>();
data.put("title", "财务报告");
data.put("detail", reportData);
Workbook workbook = ExcelExportUtil.exportExcel(params, data);
- 动态列生成
csharp
List<ExcelExportEntity> dynamicColumns = new ArrayList<>();
dynamicColumns.add(new ExcelExportEntity("动态列1", "field1"));
dynamicColumns.add(new ExcelExportEntity("动态列2", "field2"));
ExcelExportUtil.exportBigExcel(params, dynamicColumns, data);
通过上述方案,可以实现包含4级以上嵌套表头的复杂Excel导出需求。建议结合注解和动态表头构建两种方式,灵活应对不同场景的表头结构要求。实际开发中需注意数据验证和异常处理机制。