1.官网地址
2.依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
3.代码
@Slf4j
public class ImportExcelRowListener<T> implements ReadListener<T> {
/** 正文起始行(用户传入,与 EasyExcel 的 headRowNumber 保持一致) */
private final int headRowNumber;
/** 解析出的数据列表 */
private final List<T> dataList = new ArrayList<>();
/** 收集到的所有合并单元格信息
* -- GETTER --
* 外部调用:获取所有合并单元格信息
*/
@Getter
private final List<CellExtra> extraMergeInfoList = new ArrayList<>();
public ImportExcelRowListener(int headRowNumber) {
this.headRowNumber = headRowNumber;
}
/**
* 每读到一条数据就会回调
*/
@Override
public void invoke(T data, AnalysisContext context) {
dataList.add(data);
}
/**
* 读取到合并单元格、批注等额外信息时会回调
*/
@Override
public void extra(CellExtra extra, AnalysisContext context) {
if (extra.getType() == CellExtraTypeEnum.MERGE) {
if (extra.getFirstRowIndex() >= headRowNumber) {
extraMergeInfoList.add(extra);
}
}
}
/**
* 全部解析完后回调
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("所有数据解析完成,共 {} 行;合并单元格 {} 条",
dataList.size(), extraMergeInfoList.size());
}
/** 外部调用:获取原始读取的所有行数据(未填充合并) */
public List<T> getRawData() {
return dataList;
}
/**
* 外部调用:根据收集到的合并信息,回填每个并区间的值
*
* @return 已经处理好合并单元格值的 dataList
*/
public List<T> fillMergeData() {
if (extraMergeInfoList.isEmpty()) {
return dataList;
}
extraMergeInfoList.forEach(cellExtra -> {
// 计算 dataList 的行下标(减去 headRowNumber)
int r1 = cellExtra.getFirstRowIndex() - headRowNumber;
int r2 = cellExtra.getLastRowIndex() - headRowNumber;
int c1 = cellExtra.getFirstColumnIndex();
int c2 = cellExtra.getLastColumnIndex();
// 取出合并区块起点的值
Object initValue = getFieldValue(dataList.get(r1), c1);
// 区块内全部设置该值
for (int row = r1; row <= r2; row++) {
T obj = dataList.get(row);
for (int col = c1; col <= c2; col++) {
setFieldValue(obj, col, initValue);
}
}
});
return dataList;
}
/*—— 反射工具方法 ——*/
/** 根据 ExcelProperty.index 找到字段并读取值 */
private Object getFieldValue(T obj, int columnIndex) {
try {
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
com.alibaba.excel.annotation.ExcelProperty prop =
field.getAnnotation(com.alibaba.excel.annotation.ExcelProperty.class);
if (prop != null && prop.index() == columnIndex) {
return field.get(obj);
}
}
} catch (IllegalAccessException e) {
log.error("读取合并单元格初始值出错", e);
}
return null;
}
/** 根据 ExcelProperty.index 找到字段并写入值 */
private void setFieldValue(T obj, int columnIndex, Object value) {
try {
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
com.alibaba.excel.annotation.ExcelProperty prop =
field.getAnnotation(com.alibaba.excel.annotation.ExcelProperty.class);
if (prop != null && prop.index() == columnIndex) {
field.set(obj, value);
return;
}
}
} catch (IllegalAccessException e) {
log.error("填充合并单元格值出错", e);
}
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false,避免excel导入有问题
public class UserVO {
/**
* 一级
*/
@ExcelProperty(index = 0)
private String firstLevel;
/**
* 二级
*/
@ExcelProperty(index = 1)
private String secondLevel;
/**
* 三级
*/
@ExcelProperty(index = 2)
private String thirdLevel;
}
如何使用
InputStream inputStream = file.getInputStream();
int headRowNumber = 1;
ImportExcelRowListener<UserVO> listener =
new ImportExcelRowListener<>(headRowNumber);
EasyExcel.read(inputStream, UserVO.class, listener)
.extraRead(CellExtraTypeEnum.MERGE)
.sheet("TEST")
.headRowNumber(headRowNumber)
.doRead();
List<UserVO> rows = listener.fillMergeData();