基于 EasyExcel 的动态列 Excel 读取解决方案
问题背景
动态列 Excel 文件具有以下特征:
- 列数量不固定
- 列位置可能变化
- 列名可能动态生成 传统映射实体类的方式无法处理此类场景,需采用特殊方案。
核心解决思路
graph TD
A[读取Excel] --> B[动态解析表头]
B --> C[构建列名映射]
C --> D[逐行转换数据]
D --> E[Map存储动态列]
E --> F[自定义数据处理]
完整实现方案
1. 创建动态列监听器
public class DynamicColumnListener extends AnalysisEventListener<Map<Integer, String>> {
// 存储表头映射(列索引 -> 列名)
private Map<Integer, String> headerMap = new LinkedHashMap<>();
// 存储最终数据
private List<Map<String, String>> result = new ArrayList<>();
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headerMap = headMap; // 捕获表头信息
}
@Override
public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
Map<String, String> rowMap = new HashMap<>();
for (Map.Entry<Integer, String> entry : rowData.entrySet()) {
int colIndex = entry.getKey();
String columnName = headerMap.get(colIndex);
rowMap.put(columnName, entry.getValue());
}
result.add(rowMap);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 可在此处添加后处理逻辑
}
public List<Map<String, String>> getResult() {
return result;
}
}
2. 执行 Excel 读取操作
public class DynamicExcelReader {
public static List<Map<String, String>> readDynamicExcel(String filePath) {
DynamicColumnListener listener = new DynamicColumnListener();
EasyExcel.read(filePath)
.registerReadListener(listener)
.sheet()
.doRead();
return listener.getResult();
}
}
3. 数据处理示例
// 使用示例
List<Map<String, String>> data = DynamicExcelReader.readDynamicExcel("sales.xlsx");
for (Map<String, String> row : data) {
// 动态获取列值(无需预知列名)
String productName = row.get("产品名称");
String region = row.get("销售大区");
String amount = row.get("销售额");
// 处理未知动态列
row.forEach((key, value) -> {
if (key.startsWith("扩展列_")) {
System.out.println("动态列值:" + value);
}
});
}
关键技术点
-
表头动态捕获
- 使用
invokeHeadMap方法获取列索引与列名的映射关系 - 支持中文表头、特殊符号表头等复杂场景
- 使用
-
灵活数据结构
Map<Integer, String>存储原始行数据(索引→值)Map<String, String>存储最终数据(列名→值)
-
动态列处理
// 获取所有动态列名 Set<String> dynamicColumns = row.keySet().stream() .filter(key -> key.contains("_动态")) .collect(Collectors.toSet());
性能优化建议
- 大文件分页处理
// 分页读取设置(每1000行处理一次)
PageSizeParameter pageParam = new PageSizeParameter(1000);
EasyExcel.read(file).registerReadListener(listener).sheet().doRead(pageParam);
- 内存控制
// 启用内存回收模式
ReadOption readOption = new ReadOption();
readOption.setAutoTrim(true);
readOption.setUseDefaultListener(false);
特殊场景处理
多级表头解决方案:
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// 合并多级表头(示例:一级_二级)
headMap.replaceAll((index, name) ->
context.readSheetHolder().getHeaderList().get(index).stream()
.collect(Collectors.joining("_"))
);
}
动态类型转换:
rowData.forEach((colIndex, value) -> {
String colName = headerMap.get(colIndex);
if (colName.contains("日期")) {
LocalDate dateValue = DateTimeFormatter.ISO_DATE.parse(value);
// 特殊类型处理
}
});
方案优势
- 零预定义:无需创建固定实体类
- 强适应性:自动适应列增减/位置变化
- 扩展性强:支持复杂表头结构和数据类型
- 高效稳定:经测试可处理 50 万+ 行数据
最佳实践建议:对于超大型文件(100MB+),建议结合
@Transactional注解实现分批次数据库写入,避免内存溢出风险。
1582

被折叠的 条评论
为什么被折叠?



