EasyExcel导入合并单元格数据

news/2024/7/9 5:59:09 标签: java, 前端, 服务器
1.EasyExcel.read 方法

这里在read的时候要注意,增加这个extraRead(CellExtraTypeEnum.MERGE)来解析合并单元格的信息

java"> ImportListener listener = new ImportListener();
        try {
            EasyExcel.read(file.getInputStream(), ImportDto.class, listener)
                    .headRowNumber(1)
                    .extraRead(CellExtraTypeEnum.MERGE)
                    .ignoreEmptyRow(true)
                    .sheet()
                    .doRead();
        } catch (IOException e) {
            log.error("importAccountExcel-----EasyExcel.read-----error:", e);
            throw new Exception(ResponseEnum.FAILURE.getCode(), e.getMessage());
        }

        List<ImportDto> dataList = listener.getDataList();
        if (CollectionUtils.isEmpty(dataList)) {
            throw new Exception(ResponseEnum.FAILURE.getCode(), "导入文件失败,获取数据为空");
        }

2.Listener解析方法

在invok解析完所有数据以后,先将所有解析的数据存储在tempDataList中,此时合并单元格是没数据的。

然后经过convertDataMapToData方法处理完合并单元格数据后,最后才得到完整的单元格数据
 

java">@Getter
@Slf4j
public class ImportListener implements ReadListener<ImportDto> {

    private final static String[] excelTitle = {"日期", "姓名", "银行账号", "开户行", "证件号", "电话号码", "金额", "期数"};
    private final List<ImportDto> dataList = ListUtils.newArrayList();
    private final List<String> excelTitleList = Arrays.asList(excelTitle);

    /**
     * 正文起始行
     */
    private Integer headRowNumber = 1;

    /**
     * 解析的临时数据
     */
    private List<ImportDto> tempDataList = new ArrayList<>();

    /**
     * 合并单元格数据
     */
    private List<CellExtra> mergeList = new ArrayList<>();

    @Override
    public void invoke(ImportDto data, AnalysisContext context) {
//        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        checkData(data, context);
//        dataList.add(data);

        tempDataList.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("导入文件-所有数据记录:{}", JSON.toJSONString(dataList));

        // 在invok解析完数据以后,才去处理合并单元格数据,最后得到合并完以后的数据
        convertDataMapToData();

        log.info("导入文件-所有数据解析完成!");
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.info("解析失败,但是继续解析下一行:{}", exception.getMessage());

        // 如果是某一个单元格的转换异常 能获取到具体行号
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException dataException = (ExcelDataConvertException) exception;

            String errData = CellDataTypeEnum.STRING.equals(
                    dataException.getCellData().getType()) ? dataException.getCellData()
                    .getStringValue() : dataException.getCellData().getNumberValue().toPlainString();
            log.warn("第{}行,第{}列解析异常,数据为:{}", dataException.getRowIndex(), dataException.getColumnIndex(),
                    errData);

            String errorMsg = "第" +
                    dataException.getRowIndex() +
                    "行,第" +
                    dataException.getColumnIndex() +
                    "列解析异常,数据为:" +
                    errData;
            throw new BaseException(ResponseEnum.FAILURE.getCode(), errorMsg);
        } else {
            log.error("文件校验失败:", exception);

            if (exception instanceof BaseException) {
                BaseException base = (BaseException) exception;
                if (StringUtils.isNotBlank(base.getErrorMsg())) {
                    throw new BaseException(ResponseEnum.FAILURE.getCode(), base.getErrorMsg());
                }
                throw new BaseException(ResponseEnum.FAILURE.getCode(), "获取异常出错");
            } else {
                throw new BaseException(ResponseEnum.FAILURE.getCode(), "获取异常出错");
            }
        }
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        if (CollectionUtils.isEmpty(headMap)) {
            throw new BaseException(ResponseEnum.FAILURE.getCode(), "excel表头不能为空");
        }

        headMap.forEach((i, readCellData) -> {
            readCellData.checkEmpty();
            if (CellDataTypeEnum.EMPTY.equals(readCellData.getType())) {
                return;
            }
            if (!excelTitleList.contains(readCellData.getStringValue())) {
                throw new BaseException(ResponseEnum.FAILURE.getCode(), "第" + (i + 1) + "列Excel文件表头有误");
            }
        });
    }

    //上面写入的CellExtraTypeEnum.MERGE就会来调用这个方法
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        switch (extra.getType()) {
            case MERGE: // 额外信息是合并单元格
                if (extra.getRowIndex() >= headRowNumber) {
                    mergeList.add(extra); // 保存合并单元格信息
                }
                break;
            case COMMENT:
            case HYPERLINK:
            default:
        }
    }

    /**
     * 将具有多个sheet数据的dataMap转变成一个data
     */
    private void convertDataMapToData() {
        if (!CollectionUtils.isEmpty(mergeList)) {
            tempDataList = explainMergeData(tempDataList, mergeList);
        }
        dataList.addAll(tempDataList);

    }

    /**
     * 处理有合并单元格的数据
     *
     * @param list      解析的sheet数据
     * @param mergeList 合并单元格信息
     * @return 填充好的解析数据
     */
    private List<ImportDto> explainMergeData(List<ImportDto> list, List<CellExtra> mergeList) {
        // 循环所有合并单元格信息
        for (CellExtra item : mergeList) {
            Integer firstRowIndex = item.getFirstRowIndex() - headRowNumber; // 起始行
            Integer lastRowIndex = item.getLastRowIndex() - headRowNumber; // 结束行
            Integer firstColumnIndex = item.getFirstColumnIndex(); // 起始列
            Integer lastColumnIndex = item.getLastColumnIndex(); // 结束列
            // 获取初始值,即合并单元的的值
            Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, list);
            // 设置值
            for (int i = firstRowIndex; i <= lastRowIndex; i++) {
                for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {
                    setInitValueToList(initValue, i, j, list);
                }
            }
        }
        return list;
    }

    /**
     * 获取合并单元格的初始值
     * rowIndex对应list的索引
     * columnIndex对应实体内的字段
     *
     * @param firstRowIndex    起始行
     * @param firstColumnIndex 起始列
     * @param list             列数据
     * @return 初始值
     */
    private Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<ImportDto> list) {
        Object filedValue = null;
        ImportDto object = list.get(firstRowIndex);
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == firstColumnIndex) {
                    try {
                        filedValue = field.get(object);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("获取合并单元格的初始值异常:" + e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }

    /**
     * 设置合并单元格的值
     *
     * @param filedValue  值
     * @param rowIndex    行
     * @param columnIndex 列
     * @param list        解析数据
     */
    public void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<ImportDto> list) {
        ImportDto object = list.get(rowIndex);//
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true); // 提升反射性能,关闭安全检查
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == columnIndex) {
                    try {
                        field.set(object, filedValue);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的值异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}
3.导入的DTO,这里要注意index,要设置值
java">@Data
public class ImportDto implements Serializable {


    @ExcelProperty(value = "日期", converter = CustomDateConverter.class, index = 0)
    private Date putoutDate;

    @ExcelProperty(value ="姓名", index = 1)
    private String name;

    @ExcelProperty(value = "银行账号", index = 2)
    private String bankNo;

    @ExcelProperty(value = "开户行", index = 3)
    private String bankAddress;

    @ExcelProperty(value = "证件号", index = 4)
    private String idNum;

    @ExcelProperty(value = "电话号码", index = 5)
    private String cellPhone;

    @ExcelProperty(value = "金额", index = 6)
    private BigDecimal limit;
}

easyexcel导入合并单元格解析(纵向合并,横向合并都支持)_easyexcel解析合并单元格-CSDN博客


http://www.niftyadmin.cn/n/5538556.html

相关文章

万和-集训刷题1

leetcode 2 两数之和 class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode p1l1;ListNode p2l2;int next0;ListNode headnew ListNode(-1);ListNode tailhead;while (p1!null||p2!null){int n1p1!null?p1.val:0;int n2p2!null?p2.val:0;i…

7月6日 VueConf 技术大会即将在深圳举办

7月6日&#xff0c;VueConf 2024 即将在深圳召开&#xff0c;本次大会正值 Vue.js 十周年&#xff0c;旨在聚焦 Vue.js 社区的成员&#xff0c;分享最新的技术动态、经验以及创新实践。 本次参与 VueConf 大会的是来自全球 Vue.js 核心团队成员、行业专家及前端开发者。其中&a…

将CSV、Excel、XML文件转换为MySQL数据库

在平时的工作中&#xff0c;经常会遇到需要将文件数据导入到数据库中的情况。有些客户之前可能只使用Excel表格作为记录工具&#xff0c;但当数据量达到一定程度或者需要将数据导入到其他系统中时&#xff0c;就会很emo,因为Excel表格虽然方便&#xff0c;但在数据处理和管理方…

Hadoop3:Yarn的Tool接口案例

一、需求 依然以wordcount案例为基础&#xff0c;进行开发 我们知道&#xff0c;用hadoop自带的example.jar执行wordcount 命令如下 hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -D mapreduce.job.queuename…

用Redis实现排行榜的详细用例

下面是一个使用 Redis 的 Sorted Set 数据结构实现的排行榜系统的详细示例&#xff0c;包括查看全部排名、获取单个排名、增加分数等操作。我们将使用 Lettuce 库来与 Redis 进行交互。 项目结构 pom.xml&#xff1a;添加必要的依赖。LeaderBoardService&#xff1a;实现排行…

在CenteOs7上安装mysql8.0(Super详细版)

在CenteOs7上安装mysql8.0 为什么用Mysql8.0&#xff1f;如何下载下载地址需要提前准备下载步骤 服务器上安装如何上传到服务器&#xff1f;通过wget下载到服务器并解压 开始安装非必须安装如果全部安装执行顺序 安装完后&#xff0c;启动mysql使用“systemctl”检测mysqld服务…

2个方法教你轻松移除pdf文件编辑限制

PDF是一种常见的办公文档格式&#xff0c;常用于文件共享和保护。然而&#xff0c;有时候我们需要编辑PDF文件中的内容&#xff0c;但受到了编辑限制。本文将介绍一些有效的方法&#xff0c;帮助您解除PDF的编辑限制&#xff0c;轻松进行编辑和修改。 一、通过密码取消PDF“限制…

Rust学习笔记007:Trait --- Rust的“接口”

Trait 在Rust中&#xff0c;Trait&#xff08;特质&#xff09;是一种定义方法集合的机制&#xff0c;类似于其他编程语言中的接口&#xff08;java&#xff09;或抽象类(c的虚函数)。 。Trait 告诉 Rust 编译器: 某种类型具有哪些并且可以与其它类型共享的功能Trait:抽象的…