基于 Java POI 实现动态列 Excel 导出的通用方法

在企业级开发中,Excel 导出功能是常见需求。当面对前端动态传参、列数不确定的场景时,编写通用的动态列导出方法能显著提升开发效率。本文将结合 Java POI 框架,详细解析如何实现支持多级表头、动态列求和及合并单元格的 Excel 导出功能,并提供完整代码示例与使用说明。

一、核心功能特性

1. 动态列处理

  • 支持从前端接收​List<String>​类型的列名集合(​cellList​),结合固定列数组(​strCloumnAry​)动态拼接最终列集合
  • 使用​LinkedHashMap​维护列顺序,确保导出列与传入顺序一致

2. 多级表头支持

  • 通过​headerNum​参数控制表头层级(1-3 级)
  • 表头名称通过​||​分隔(如​"船舶信息||完船日期||吨位"​),自动解析为多级结构

3. 数据统计功能

  • 自动识别数值型列(​Number​类型),生成​totalMap​存储列求和结果
  • 支持在导出数据末尾添加 "合计" 行,并合并单元格显示

4. 样式与布局优化

  • 统一设置单元格边框、居中对齐、字体大小
  • 自动调整列宽适应内容
  • 多级表头行合并相同标题单元格,提升可读性

二、关键代码解析

1. 列集合初始化

ini 复制代码
LinkedHashMap<String, Integer> allColumn = new LinkedHashMap<>();
// 先添加固定列
for (int i = 0; i < strCloumnAry.length; i++) {
    allColumn.putIfAbsent(strCloumnAry[i], i);
}
// 再添加动态列(避免重复)
int i = strCloumnAry.length;
for (String key : cellList) {
    if (!allColumn.containsKey(key)) {
        allColumn.put(key, i++);
    }
}
  • 使用​LinkedHashMap​保持顺序,固定列在前,动态列在后
  • 通过​putIfAbsent​避免重复列名导致的索引冲突

2. 多级表头生成

ini 复制代码
if (headerNum == 2) { // 二级表头处理
    for (String key : allColumn.keySet()) {
        String[] parts = key.split("\|\|"); // 按||拆分
        if (parts.length == 2) {
            // 一级表头行
            Cell cell1 = headerRow1.createCell(allColumn.get(key));
            cell1.setCellValue(parts[0]);
            // 二级表头行
            Cell cell2 = headerRow2.createCell(allColumn.get(key));
            cell2.setCellValue(parts[1]);
        }
    }
}
  • 通过​split("\|\|")​解析多级表头名称
  • 根据​headerNum​创建对应行数的表头行(1-3 级)
  • 同一列的多级表头单元格共享相同列索引

3. 数据行与合计行

less 复制代码
// 写入数据行
int row = headerNum; // 表头行下方开始写数据
for (Map<String, Object> data : datas) {
    Row dataRow = sheet.createRow(row++);
    for (Map.Entry<String, Integer> entry : allColumn.entrySet()) {
        Cell cell = dataRow.createCell(entry.getValue());
        cell.setCellValue(data.get(entry.getKey()) != null ? data.get(entry.getKey()).toString() : "");
    }
}

// 写入合计行
Row totalRow = sheet.createRow(row);
totalRow.createCell(0).setCellValue("合计");
for (Map.Entry<String, Object> entry : totalMap.entrySet()) {
    int colIndex = allColumn.get(entry.getKey());
    Cell cell = totalRow.createCell(colIndex);
    cell.setCellValue(entry.getValue() != null ? entry.getValue().toString() : "");
}
// 合并合计行标题单元格
sheet.addMergedRegion(new CellRangeAddress(row, row, 0, maxSp));
  • ​totalMap​通过遍历数据行自动计算数值列总和
  • 合计行标题 "合计" 通过​CellRangeAddress​合并多个列单元格
  • ​maxSp​变量动态计算需要合并的列范围

4. 单元格样式设置

ini 复制代码
// 创建通用样式
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);

// 表头字体设置
Font headerFont = workbook.createFont();
headerFont.setFontHeightInPoints((short) 10);
cellStyle.setFont(headerFont);
  • 统一设置水平 / 垂直居中对齐
  • 添加细边框样式
  • 表头使用 10 号字体

三、使用示例

1. 方法参数说明

参数名 类型 说明
response HttpServletResponse Servlet 响应对象,用于输出文件流
datas List<Map<String,Object>> 导出数据集合,每个 Map 代表一行数据
cellList List 动态列名集合(如前端勾选的列)
baseFileName String 基础文件名(不含时间戳)
strAry String[] 固定列显示名称数组(与 strCloumnAry 对应)
strCloumnAry String[] 固定列字段名数组(如数据库字段名)
headerNum int 表头层级(1-3 级)

2. 调用示例

ini 复制代码
// 假设前端传入动态列名为["price","quantity"]
List<String> cellList = Arrays.asList("price", "quantity");
// 固定列字段名与显示名
String[] strCloumnAry = {"name", "date"};
String[] strAry = {"商品名称", "日期"};

// 构造模拟数据
List<Map<String, Object>> datas = new ArrayList<>();
Map<String, Object> data1 = new HashMap<>();
data1.put("name", "苹果");
data1.put("date", "2023-10-01");
data1.put("price", 5.99);
data1.put("quantity", 100);
datas.add(data1);

// 调用导出方法(二级表头示例)
exportDynamicExcel(response, datas, cellList, "商品销售报表", strAry, strCloumnAry, 2);

3. 表头命名规范

  • 多级表头通过​||​分隔,如:
  • 一级表头:​"销售额"​
  • 二级表头:​"季度数据||销售额"​
  • 三级表头:​"年度汇总||季度数据||销售额"​

四、优化与扩展建议

1. 类型适配优化

当前代码仅处理​​Number​​类型数值求和,可扩展支持:

  • ​String​类型数值转换(需校验格式)
  • ​LocalDate​/​LocalDateTime​类型日期格式化显示

2. 样式扩展

  • 添加条件格式(如数值超过阈值时标红)
  • 支持不同背景色区分奇偶行
  • 表头行添加自动筛选功能

3. 性能优化

  • 大数据量场景可改用​SXSSFWorkbook​(基于磁盘缓存的流式处理)
  • 合并单元格操作可批量处理,减少​sheet.addMergedRegion​调用次数

4. 错误处理增强

  • 添加参数校验(如​cellList​​strCloumnAry​重复校验)
  • 提供更友好的异常提示信息(如字段名不存在)

五、总结

本文提供的动态列 Excel 导出方法通过灵活的参数设计,实现了:

  • 动态列与固定列的混合展示
  • 1-3 级多级表头的自动解析
  • 数值列自动求和与合计行生成
  • 统一的单元格样式与布局优化

适用于需要根据用户选择动态展示列的管理系统(如数据报表、权限控制场景)。实际使用中可根据业务需求进一步扩展类型适配、样式定制等功能,提升导出文件的实用性与美观性。

相关推荐
鬼火儿4 小时前
SpringBoot】Spring Boot 项目的打包配置
java·后端
cr7xin4 小时前
缓存三大问题及解决方案
redis·后端·缓存
间彧5 小时前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧5 小时前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
间彧5 小时前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧5 小时前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端
间彧5 小时前
如何为Docker Compose中的服务配置健康检查,确保服务真正可用?
后端
间彧5 小时前
Docker Compose和Kubernetes在编排服务时有哪些核心区别?
后端
间彧5 小时前
如何在实际项目中集成Arthas Tunnel Server实现Kubernetes集群的远程诊断?
后端
brzhang6 小时前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构