easyExcel 数据导入

前言

这段时间做了excel 数据导入,用的阿里巴巴的easyExcel 的代码。下面是官网和githab 代码地址。需要将github 上的代码拉下来,方便查看demo.关于Easyexcel | Easy ExcelEasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目,在尽可能节约内存的情况下支持读写百M的Excel。https://easyexcel.opensource.alibaba.com/docs/current/

GitHub - alibaba/easyexcel: 快速、简洁、解决大文件内存溢出的java处理Excel工具快速、简洁、解决大文件内存溢出的java处理Excel工具. Contribute to alibaba/easyexcel development by creating an account on GitHub.https://github.com/alibaba/easyexcel

 仔细查看上面的代码,模仿上面的代码就可以写出来了。

操作步骤

一,创建对象

对象不能使用@Accessors(chain = true) 注解,不然会出现读取不到数据的情况

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

二,随便创建一个简单的类,不需要任何配置。

只需要根据官网demo来实现或者继承他们自己的内部类即可,创建的类放哪里都行

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener {    

}

三,了解继承类的各个方法

3.1 invokeHead 方法是用来读取表头的。

 @Override
    public void invokeHead(Map> headMap, AnalysisContext context) {}

使用invokeHead 方法需要我们先定义哪几行是表头,下面代码中的.headRowNumber(3) 可以定义哪几行是表头。3待办前三行都是表头,invokeHead 方法会读取前三行,我们可以根据invokeHead 来校验表头

EasyExcel.read(file.getInputStream(), YclJzLqJcVo.class, yclJzLqExcelDataListener).sheet().headRowNumber(3).doRead();

3.2 invoke 是用来读取数据内容的,在invokeHead 方法运行完后就会运行invoke 方法。来读取数据内容。

@Override
public void invoke(YclJzLqJcVo data, AnalysisContext context) {}

3.3  所有数据解析完成了 都会来调用

@Override
public void doAfterAllAnalysed(AnalysisContext context) {}

四,导入并且返回异常数据提示信息

4.1 实体类设置 

@ExcelProperty(index = 1, value = "进场日期")

 @ExcelProperty注解中的index 属性代表该字段读取的excel 上的第几列,index =1 代表该字段读取excel 上的第二列,index =0 读取第一列。

easyExcel 数据导入_第1张图片

package easttrans.pitchdatacore.domain.core;

import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.Date;

/**
 * 

* *

* * @author guankong * @since 2022-10-26 */ @Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="改性沥青模板映射类", description="") public class YclGxLqJcVo extends CommonVo implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "id") @TableId(value = "id", type = IdType.AUTO) private Integer id; @ExcelProperty(index = 0, value = "序号") @ApiModelProperty(value = "序号") private Integer serialNum; @ExcelProperty(index = 1, value = "进场日期") @ApiModelProperty(value = "进场日期") private Date enterDate; @ExcelProperty(index = 2, value = "品牌及标号") @ApiModelProperty(value = "品牌及标号") private String brand; @ExcelProperty(index = 3, value = "堆放地点") @ApiModelProperty(value = "堆放地点") private String place; @ExcelProperty(index = 4, value = "用途") @ApiModelProperty(value = "用途面层") private String ratio; @ExcelProperty(index = 5, value = "质保单编号") @ApiModelProperty(value = "质保单编号") private String qaNum; @ExcelProperty(index = 6, value = "同批数量(t)") @ApiModelProperty(value = "同批数量(t)") private Double count; @ExcelProperty(index = 7, value = "委托单或任务单编号") @ApiModelProperty(value = "委托单或任务单编号") private String taskNum; @ExcelProperty(index = 8, value = "针入度0.1mm") @ApiModelProperty(value = "针入度0.1mm") private Double zrd; }

4.2  拦截器设置

这里AnalysisEventListener 可以设置成泛型AnalysisEventListener


@Slf4j
public final  class YclJzLqExcelDataListener extends AnalysisEventListener {

    private final YclLqJcVo yclLqJcVo;

    private YclLqJcService yclLqJcService;
    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 存储异常提醒消息
     */
    private Map result = new HashMap<>();
    /**
     * excel表头是否正确,false 有误,true 正确。
     */
    private boolean format = false;

    /**
     * 头信息
     */
    Map headMap = new HashMap<>();

    /**
     * 返回提示语
     */
    public List tips = new ArrayList<>();

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param
     */
    public YclJzLqExcelDataListener(YclLqJcService yclLqJcService,YclLqJcVo yclLqJcVo) {
        this.yclLqJcService = yclLqJcService;
        this.yclLqJcVo = yclLqJcVo;
    }


    /**
     * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context)   {
        log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        int col = 0,row =0;
        String title = "";
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
                    excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
            col = excelDataConvertException.getColumnIndex();
            row = excelDataConvertException.getRowIndex();
            title = this.headMap.get(col);

        }
    }

    @Override
    public void invokeHeadMap(Map headMap, AnalysisContext context) {
        this.headMap = headMap;
    }


    /**
     * 这里会一行行的返回头
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map> headMap, AnalysisContext context) {
        log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
        // 如果想转成 Map
        // 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener
        // 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换
        Map integerStringMap = ConverterUtils.convertToStringMap(headMap, context);
        String s = integerStringMap.get(0);
        if (s != null && s.equals(Constants.JZ_Asp_Mob_Ins_Account)){
            //如果表头包含该内容,则为true
            format = true;
        }
        
    }

    @Override
    public void invoke(YclJzLqJcVo data, AnalysisContext context) {
        result.put("format",format);
        if (!format){
            //format 为false 时
            throw new ExcelAnalysisStopException(Constants.formatTemplateNoTrue);
        }

        //查询改性沥青数据库,判断该数据库中的报告编号是否与cachedDataList 中的报告编号重复,重复不导入
        LambdaQueryWrapper yclJzLqJcVoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        yclJzLqJcVoLambdaQueryWrapper.eq(YclLqJcVo::getReportNum,data.getReportNum());
        yclJzLqJcVoLambdaQueryWrapper.eq(YclLqJcVo::getType,Constants.JZLQ_TYPE);

        List list = yclLqJcService.list(yclJzLqJcVoLambdaQueryWrapper);
        if (list.size()>0){
            //表示有数据,该数据不能新增进数据库
            saveTips(context.readRowHolder().getRowIndex(),Constants.Duplicate_Import_Information,tips);
            return;
        }

        
        cachedDataList.add(data);
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();

        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
    
        yclLqJcService.saveBatch(collect);
        log.info("存储数据库成功!");
    }

    /**
     * 返回数据
     * @return 返回读取的数据集合
     **/
    public Map getResult(){
        return result;
    }

    /**
     * 设置读取的数据集合
     * @param result 设置读取的数据集合
     **/
    public void setResult(Map result) {
        this.result = result;
    }

  

}

4.3 controller 层设置


    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    @ApiOperation(value = "沥青进场检验台账导入数据", notes = "沥青进场检验台账导入数据")
    public EastTransResult importExcel(@RequestParam("file") MultipartFile file, YclLqJcVo yclLqJcVo) throws IOException {
        EastTransResult eastTransResult = new EastTransResult<>();
        //两个判断,第一个是表头,第二个是内容(哪一行的数据不对)
            //type 等于2 为改性沥青,先判断第一行导入模板的表头是否准确
            YclGxLqExcelDataListener yclGxLqExcelDataListener = new YclGxLqExcelDataListener(yclLqJcService, yclLqJcVo);
            EasyExcel.read(file.getInputStream(), YclGxLqJcVo.class, yclGxLqExcelDataListener).sheet().headRowNumber(3).doRead();
            Map result = yclGxLqExcelDataListener.getResult();
            boolean format = (boolean) result.get("format");
            eastTransResult = new EastTransResult(200, null, "请求成功");
            if (!format) {
                //表头错误
                eastTransResult = new EastTransResult(403, null, "该模板不正确");
            }

        }
        return eastTransResult;
    } 
  

返回的数据是 Map result ,可以看下listener 类里面的result 是怎么定义的,先创建了一个 

private Map result = new HashMap<>();

 然后设置了该result 的get,set 方法。

public Map getResult(){
    return result;
}

public void setResult(Map result) {
    this.result = result;
}

 然后可以在listener 类里面result.put("key","val");

后面在controller 中获取该类listener 的getResult 方法就可以获取这个map 了。

如果数据导入太慢,可以使用多线程【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据_小z♂的博客-CSDN博客_springboot 线程池 写入数据springboot使用EasyExcel和线程池实现多线程导入Excel数据https://blog.csdn.net/weixin_56995925/article/details/125944749

java实现excel的导入导出(带参数校验:非空校验、数据格式校验)_Mr_yu_shao的博客-CSDN博客_java导入excel怎样校验经过简单的封装完成了一个带参数校验的简单使用案例,直接引入项目即用https://blog.csdn.net/Mr_yu_shao/article/details/126459849 

你可能感兴趣的:(excel)