easyExcel导入多sheet的Excel,存在合并单元格情况

发布于:2025-06-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

1.官网地址

easyExcel官方文档

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();

网站公告

今日签到

点亮在社区的每一天
去签到