java demo(三) easyexcel初次尝试

easyexcel初次尝试

交代业务背景:
我需要去读取excel中合并单元格的数据记录,如果是导出格式这里应该没有,因为我觉得生成excel文件相对来说比较容易,也许初生牛犊不怕虎吧。
下面是我借鉴的博客:
作者:mybabe0312

业务需求:

讲解一下具体的需要:我需要拿到用户信息和他每天需要吃饭的次数,用于给这个用户预定饭,不然他就得饿着了。所以这里明确的是:时间2021/7/1是动态的,然后就是下面的早餐、午餐、晚餐也是动态的,也就意味着我不知道他要预定几天的的,每天的餐段又是多少,这是未知,也是这个的难点。

如果你刚刚好看到了这篇文章,而你也有类似的问题,你可以借鉴我的思路,完成你的需求啦。我用了一天才弄懂这个,有点笨。。。
这就是我的需求

我添加的maven依赖

<!--excel 读写操作 主要的--> 
<dependency>
   	<groupId>com.alibaba</groupId>
   	<artifactId>easyexcel</artifactId>
   	<version>2.2.6</version>
</dependency>
        
其他依赖就不给你了,自己动手丰衣足食

我读取到的数据

在这里插入图片描述这个是合并单元格的属性 如果你的合并单元格和我的不同,你也可以根据这个来设置你自己的规则,这叫做举一反三哦:
在这里插入图片描述

代码展示

实体类 主要信息记录
@Data
@EqualsAndHashCode(callSuper = false)
public class StudentExcel {


    /**
     * 用户姓名
     */
    private String name;

    /**
     * 用户工号
     */
    private String employeeNo;

    /**
     * 用户报餐信息
     */
    private List<PeriodInfoDTO> list;
}
实体类 记录日期对应餐段的数据
@Data
public class PeriodInfoDTO {

    /**
     * 日期 2021-07-15
     */
    private Date time;

    /**
     * 餐段名称
     */
    private String periodName;

    /**
     * 报餐次数
     */
    private Integer count;
}
业务实现代码 main方法内的可以提出来具体实现
package com.demo.utils;

import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.CellExtra;
import com.demo.entity.excel.PeriodInfoDTO;
import com.demo.entity.excel.StudentExcel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 描述:监听
 *
 * @author XxX
 * @date 2021-07-15 16:58
 */
@Component
public class StudentExcelListener extends AnalysisEventListener<Map<Integer, String>> {

    /**
     * Integer:第几行数据index, Map<Integer:第几个单元格的index, String:值> index 从0开始
     */
    private final LinkedHashMap<Integer, Map<Integer, String>> dataMap = new LinkedHashMap<>();

    /**
     * 合并单元格的附加属性
     */
    private final List<CellExtra> extraMergeInfoList = new ArrayList<>();

    /**
     * 最终结果存放的地方
     */
    private List<StudentExcel> resultList = new ArrayList<>();


    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        //读取到的每行数据,其key是以0开始的索引
        dataMap.put(context.readRowHolder().getRowIndex(),data) ;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        this.explainMergeData(dataMap, extraMergeInfoList);
    }

    /**
     * 我制定的规则
     *
     * @param data                  读取excel的数据存储地方
     * @param extraMergeInfoList    何必单元格的附加属性
     */
    private void explainMergeData(LinkedHashMap<Integer, Map<Integer, String>> data, List<CellExtra> extraMergeInfoList) {
        if (CollectionUtils.isEmpty(extraMergeInfoList)) {
            return;
        }
        //和单元格的时间格式需要对应 因为需要转译
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        //第一行和第二行数据,这是必定存在的标题头
        Map<Integer, String> title1 = data.get(0);
        Map<Integer, String> title2 = data.get(1);

        //获取日期和餐段信息
        Map<Integer, PeriodInfoDTO> periodInfoMap = new HashMap<>();
        //只需要合并行的记录 合并列的我不需要
        Map<Integer, CellExtra> extraMap = extraMergeInfoList.stream()
                //想要过滤得到日期的单元格附加属性
                .filter(p -> (p.getFirstColumnIndex() != 0 || p.getLastColumnIndex() != 0) && (!p.getFirstColumnIndex().equals(p.getLastColumnIndex())))
                .collect(Collectors.toMap(CellExtra::getColumnIndex, Function.identity()));

        PeriodInfoDTO periodInfo;
        for (Map.Entry<Integer, String> elm : title1.entrySet()) {
            if (StringUtils.isBlank(elm.getValue())) {
                continue;
            }
            CellExtra extra = extraMap.get(elm.getKey());
            if (extra != null) {
                String date = elm.getValue();
                //包含这个列 表示获取的是日期 要去第二行找
                int firstColumnIndex = extra.getFirstColumnIndex();
                int lastColumnIndex = extra.getLastColumnIndex();
                while (firstColumnIndex <= lastColumnIndex) {
                    periodInfo = new PeriodInfoDTO();
                    periodInfo.setTime(DateUtil.parse(date, sdf));
                    periodInfo.setPeriodName(title2.get(firstColumnIndex));
                    periodInfoMap.put(firstColumnIndex, periodInfo);
                    firstColumnIndex++;
                }
            }
        }
        StudentExcel student;
        PeriodInfoDTO newDTO;
        PeriodInfoDTO periodInfoDTO;
        List<PeriodInfoDTO> list;
        List<StudentExcel> studentExcels = new ArrayList<>(data.size());
        for (Map.Entry<Integer, Map<Integer, String>> elm : data.entrySet()) {
            if (elm.getKey() <= 1) {
                continue;
            }
            student = new StudentExcel();
            list = new ArrayList<>();
            for (Map.Entry<Integer, String> elm1 : elm.getValue().entrySet()) {
                switch (elm1.getKey()) {
                    case 1:
                        student.setName(elm1.getValue());
                        break;
                    case 2:
                        student.setEmployeeNo(elm1.getValue());
                        break;
                    default:
                        periodInfoDTO = periodInfoMap.get(elm1.getKey());
                        if (periodInfoDTO != null) {
                            newDTO = new PeriodInfoDTO();
                            newDTO.setTime(periodInfoDTO.getTime());
                            newDTO.setPeriodName(periodInfoDTO.getPeriodName());
                            newDTO.setCount(Integer.valueOf(elm1.getValue()));
                            list.add(newDTO);
                        }
                        break;
                }
            }
            student.setList(list);
            studentExcels.add(student);
        }
        this.resultList = studentExcels;
    }

    /**
     * 区分extra是什么类型
     *
     * @param extra 单元格附加属性 这一块没仔细研究呀
     * @param context 内容 a context is the main anchorage point of a excel reader
     */
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        switch (extra.getType()){
            case MERGE:{
                //合并单元格
                extraMergeInfoList.add(extra);
                break;
            }
            // HYPERLINK 超链接 COMMENT 评论
            default: {
                break;
            }
        }
    }

    /**
     * 某行的数据解析失败
     * */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        System.err.println("解析失败,但是继续解析下一行: " + exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            //这里直接抛出异常 提示哪行数据问题,方便定位
            System.err.println("第{}行,第{}列解析异常" + excelDataConvertException.getRowIndex() +
                    excelDataConvertException.getColumnIndex());
        }
    }

    public static void main(String[] args) {
        //读取的表格的绝对路径
        String fileName3 = "/Downloads/c1.xlsx" ;
        StudentExcelListener readListener = new StudentExcelListener();
        EasyExcel.read(fileName3, readListener)
                // 需要读取合并单元格信息 默认不读取 需要指定
                .extraRead(CellExtraTypeEnum.MERGE)
                //.registerConverter(new EmptyConverter()) //默认:DefaultConverterLoader#loadDefaultReadConverter()
                .ignoreEmptyRow(true)
                .autoTrim(true)
                //默认从第一行开始读 需要指定
                .headRowNumber(0)
                .autoCloseStream(true)
                //.sheet("读取指定的sheet")
                .sheet()
                .doRead();
        List<StudentExcel> resultList = readListener.resultList;
        System.out.println("你好");
    }
}

结束语

这里是最后了,主要难点就是怎么取获取数据和合并单元格的属性,其实一开始什么都不知道,到后面的一知半解,但是这还不够,这一次算是初次学习和总结,收获了很多,但是也看到了自己对java的了解远远不够,继续加油!!

最后提醒一下哈,主要的规则根据参数来界定和拼接你需要的数据记录,这算是一个不是很好的小demo,但是可以给你一点小见解。重点关注-> explainMergeData方法哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值