JAVA实现easyExcel批量导入

这篇具有很好参考价值的文章主要介绍了JAVA实现easyExcel批量导入。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

注解 类型 描述
ExcelProperty 导入 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
ExcelIgnore 导入 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
DateTimeFormat 导入 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
NumberFormat 导入 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat
ExcelIgnoreUnannotated 导入 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与

导入方法参数:ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级。

  • converter 转换器,默认加载了很多转换器。也可以自定义。
  • readListener 监听器,在读取数据的过程中会不断的调用监听器。
  • headRowNumber 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。
  • headclazz二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。
  • clazzhead二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。
  • autoTrim 字符串、表头等数据自动trim
  • password 读的时候是否需要使用密码

ReadWorkbook(理解成excel对象)参数

  • excelType 当前excel的类型 默认会自动判断

  • inputStreamfile二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用file参数。因为使用了inputStream easyexcel会帮忙创建临时文件,最终还是file

  • fileinputStream二选一。读取文件的文件。

  • autoCloseStream 自动关闭流。

  • readCache 默认小于5M用 内存,超过5M会使用 EhCache,这里不建议使用这个参数。

  • useDefaultListener@since 2.1.4默认会加入ModelBuildEventListener来帮忙转换成传入class的对象,设置成false后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>对象,如果还想继续接听到

    class对象,请调用readListener方法,加入自定义的beforeListener、ModelBuildEventListener、 自定义afterListener即可。

ReadSheet(就是excel的一个Sheet)参数

  • sheetNo 需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet
  • sheetName 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配

添加pom依赖

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>easyexcel</artifactId>
   <version>2.2.6</version>
</dependency>
<!--工具类-->
<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.8.23</version>
</dependency>
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.21</version>
</dependency>
<!--commons依赖  -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

第一种:简单导入

实体类

package com.example.mybatismysql8demo.excel;

import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.math.BigDecimal;

@Data
//忽视无注解的字段
@ExcelIgnoreUnannotated
public class GoodsImportExcel {

    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty(value = {"商品信息","商品名称"},index = 0)
    public String goodsName;

    @ExcelProperty(value = {"商品信息","商品价格"},index = 1)
    public BigDecimal price;

    @ExcelProperty(value = {"商品信息","商品数量"},index = 2)
    public Integer num;
    
}

监听器

package com.example.mybatismysql8demo.handler;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;
import java.util.function.Consumer;

/**
 * 读取excel数据
 */
public class DemoDataListener extends AnalysisEventListener<GoodsImportExcel> {

    /**临时存储正常数据集合,最大存储100*/
    private List<GoodsImportExcel> successDataList = Lists.newArrayListWithExpectedSize(100);

    /**自定义消费者函数接口用于自定义监听器中数据组装*/
    private final Consumer<List<GoodsImportExcel>> successConsumer;

    public DemoDataListener(Consumer<List<GoodsImportExcel>> successConsumer) {
        this.successConsumer = successConsumer;
    }

    @Override
    public void invoke(GoodsImportExcel goodsImportExcel, AnalysisContext analysisContext) {
        successDataList.add(goodsImportExcel);
        System.out.println("数据:"+goodsImportExcel);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (CollectionUtils.isNotEmpty(successDataList)) {
            successConsumer.accept(successDataList);
        }
    }
}

执行方法

package com.example.mybatismysql8demo.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSONObject;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.example.mybatismysql8demo.handler.DemoDataListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;


@Slf4j
@RestController
public class EasyExcelController {

    @PostMapping("/easyExcelImport")
    public void importExcel(MultipartFile file,Integer type) {
        if (!file.isEmpty()) {
            //文件名称
            int begin = Objects.requireNonNull(file.getOriginalFilename()).indexOf(".");
            //文件名称长度
            int last = file.getOriginalFilename().length();
            //判断文件格式是否正确
            String fileName = file.getOriginalFilename().substring(begin, last);
            if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
                throw new IllegalArgumentException("上传文件格式错误");
            }
        } else {
            throw new IllegalArgumentException("文件不能为空");
        }
        try (InputStream inputStream = file.getInputStream()) {
            if (type == 1){
                simpleRead(inputStream);
            }else if (type == 2){
                synchronousRead(inputStream);
            }else {
                repeatedRead(inputStream);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 最简单的读的监听器
     */
    public void simpleRead(InputStream inputStream){
        //获取正确数据
        ArrayList<GoodsImportExcel> successArrayList = new ArrayList<>();
        EasyExcel.read(inputStream)
                .head(GoodsImportExcel.class)
                .registerReadListener(new DemoDataListener(
                        // 监听器中doAfterAllAnalysed执行此方法;所有读取完成之后处理逻辑
                        successArrayList::addAll))
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题(字段列表)所在行数
                .headRowNumber(2)
                .doReadSync();
        System.out.println(successArrayList);
    }

    /**
     * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
     */
    public void synchronousRead(InputStream inputStream){
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
        List<GoodsImportExcel> batchGoodsImportModels = EasyExcel.read(inputStream)
                .head(GoodsImportExcel.class)
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题(字段列表)所在行数
                .headRowNumber(2)
                .doReadSync();
        System.out.println(JSONObject.toJSONString(batchGoodsImportModels));
    }

    /**
     * 读取多个sheet
     */
    public void repeatedRead(InputStream inputStream){
        ArrayList<GoodsImportExcel> successArrayList = new ArrayList<>();
        //使用模型来读取Excel(多个sheet)
        ExcelReader reader = EasyExcel.read(inputStream).build();
        //多个sheet
        List<ReadSheet> sheetList = new ArrayList<>();
        for (int i = 0; i < reader.getSheets().size(); i++){
            // 这里为了简单,所以注册了同样的head 和Listener 自己使用功能必须不同的Listener
            ReadSheet readSheet = EasyExcel.readSheet(i)
                    .head(GoodsImportExcel.class)
                    .registerReadListener(new DemoDataListener(successArrayList::addAll))
                    // 设置标题(字段列表)所在行数
                    .headRowNumber(2)
                    .build();
            sheetList.add(readSheet);
        }
        // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
        reader.read(sheetList);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        reader.finish();
        System.out.println(successArrayList);
    }
    
}

结果打印
数据:GoodsImportExcel(goodsName=苹果, price=10, num=11)
数据:GoodsImportExcel(goodsName=香蕉, price=8, num=12)
数据:GoodsImportExcel(goodsName=梨子, price=11.0, num=30)
数据:GoodsImportExcel(goodsName=葡萄, price=20.0, num=40)
[GoodsImportExcel(goodsName=苹果, price=10, num=11), GoodsImportExcel(goodsName=香蕉, price=8, num=12), GoodsImportExcel(goodsName=梨子, price=11.0, num=30), GoodsImportExcel(goodsName=葡萄, price=20.0, num=40)]

导入模版
JAVA实现easyExcel批量导入,项目技术框架,java,后端,excel
JAVA实现easyExcel批量导入,项目技术框架,java,后端,excel

第二种:数据校验

自定义注解

package com.example.mybatismysql8demo.config;

import java.lang.annotation.*;


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface LengthValid {

    int length() default 0;

    String msg() default "";

    int cell() default 0;
}

实体类

package com.example.mybatismysql8demo.excel;

import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.example.mybatismysql8demo.config.LengthValid;
import lombok.Data;

import java.math.BigDecimal;

@Data
//忽视无注解的字段
@ExcelIgnoreUnannotated
public class GoodsImportExcel {

    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @LengthValid(length =  5,msg = "商品名称长度超出5个字符串!",cell = 1)
    @ExcelProperty(value = {"商品信息","商品名称"},index = 0)
    public String goodsName;

    @ExcelProperty(value = {"商品信息","商品价格"},index = 1)
    public BigDecimal price;

    @ExcelProperty(value = {"商品信息","商品数量"},index = 2)
    public Integer num;

    private String errorMsg;
}

监听器

package com.example.mybatismysql8demo.handler;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.Assert;
import com.example.mybatismysql8demo.config.LengthValid;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * 读取excel数据
 */
@Slf4j
public class DemoDataListener extends AnalysisEventListener<GoodsImportExcel> {

    /**单次处理上限100条记录*/
    private static final int BATCH_COUNT = 100;

    /**临时存储正常数据集合*/
    private List<GoodsImportExcel> successDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);

    /**临时存错误储数据集合*/
    private List<GoodsImportExcel> errorDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);

    /**自定义消费者函数接口用于自定义监听器中数据组装*/
    private final Consumer<List<GoodsImportExcel>> successConsumer;

    private final Consumer<List<GoodsImportExcel>> errorConsumer;

    public DemoDataListener(Consumer<List<GoodsImportExcel>> successConsumer, Consumer<List<GoodsImportExcel>> errorConsumer) {
        this.successConsumer = successConsumer;
        this.errorConsumer = errorConsumer;
    }

    /**手机号格式异常日志处理*/
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("异常信息:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号,如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }else if (exception instanceof IllegalArgumentException){
            throw new IllegalArgumentException(exception.getMessage());
        }
    }

    /**
     * 在这里进行模板的判断
     * @param headMap 存放着导入表格的表头,键是索引,值是名称
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //只校验第三行表头是否正确
        Integer rowNum = context.getCurrentRowNum();
        if (rowNum == 2) {
            // 获取数据实体的字段列表
            Field[] fields = GoodsImportExcel.class.getDeclaredFields();
            // 遍历字段进行判断
            for (Field field : fields) {
                // 获取当前字段上的ExcelProperty注解信息
                ExcelProperty fieldAnnotation = field.getAnnotation(ExcelProperty.class);
                // 判断当前字段上是否存在ExcelProperty注解
                if (fieldAnnotation != null) {
                    String value = fieldAnnotation.value()[1];
                    // 存在ExcelProperty注解则根据注解的value值到表格中对比是否存在对应的表头
                    if(!headMap.containsValue(value)){
                        // 如果表格不包含模版类字段中的表头,则抛出异常不再往下执行
                        throw new RuntimeException("模板错误,请检查导入模板");
                    }
                }
            }
        }
    }


    /**每行读取监听触发逻辑*/
    @SneakyThrows
    @Override
    public void invoke(GoodsImportExcel goodsImportExcel, AnalysisContext analysisContext) {
        //获取总行数
        Integer rowNumber = analysisContext.readSheetHolder().getApproximateTotalRowNumber();
        //行数
        int row = analysisContext.readRowHolder().getRowIndex();
        log.info("第" + row + "行数据进行处理");
        // 手机号格式校验
        validParam(goodsImportExcel,row);
        //正常数据
        successDataList.add(goodsImportExcel);
        // 按照指定条数对导入数据进行分批处理
        if (successDataList.size() >= BATCH_COUNT) {
            successConsumer.accept(successDataList);
            successDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    private void validParam(GoodsImportExcel goodsImportExcel, int row) throws IllegalAccessException {
        // 参数校验
        Field[] fields = goodsImportExcel.getClass().getDeclaredFields();
        for (Field field : fields) {
            //设置可访问
            field.setAccessible(true);
            //判断字段是否添加校验
            boolean valid = field.isAnnotationPresent(LengthValid.class);
            if (valid) {
                //获取注解信息
                LengthValid annotation = field.getAnnotation(LengthValid.class);
                //行数列数
                String msg = "第" + row + "行的第" + annotation.cell() + "列:";
                //值
                String value = (String) field.get(goodsImportExcel);
                if(value.length() > annotation.length()){
                    //错误信息
                    goodsImportExcel.setErrorMsg(msg + annotation.msg());
                    //错误数据
                    errorDataList.add(goodsImportExcel);
                    // 按照指定条数对导入数据进行分批处理
                    if (errorDataList.size() >= BATCH_COUNT) {
                        errorConsumer.accept(errorDataList);
                        errorDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
                    }
                    throw new RuntimeException(msg + annotation.msg());
                }
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (CollectionUtils.isNotEmpty(successDataList)) {
            successConsumer.accept(successDataList);
        }
        if (CollectionUtils.isNotEmpty(errorDataList)) {
            errorConsumer.accept(errorDataList);
        }
    }

    /**
     * 额外信息(批注、超链接、合并单元格信息读取)
     */
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        log.info("读取到了一条额外信息:{}", JSONObject.toJSONString(extra));
        switch (extra.getType()) {
            case COMMENT:
                log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(), extra.getText());
                break;
            case HYPERLINK:
                if ("Sheet1!A1".equals(extra.getText())) {
                    log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(), extra.getText());
                } else if ("Sheet2!A1".equals(extra.getText())) {
                    log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}," + "内容是:{}",
                            extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                            extra.getLastColumnIndex(), extra.getText());
                } else {
                    Assert.fail("Unknown hyperlink!");
                }
                break;
            case MERGE:
                log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
                        extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                        extra.getLastColumnIndex());
                break;
            default:
        }
    }

    /**
     *监听器的hasNext()方法时没有注意到默认返回的是false,导致一进监听器就判断已经没有下一条记录,直接跳出监听器,然后导入就完成,也不会报错,改成返回true即可解决
     */
    @Override
    public boolean hasNext(AnalysisContext analysisContext) {
        return true;
    }
}

执行方法

package com.example.mybatismysql8demo.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSONObject;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.example.mybatismysql8demo.handler.DemoDataListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;


@Slf4j
@RestController
public class EasyExcelController {

    @PostMapping("/easyExcelImport")
    public void importExcel(MultipartFile file,Integer type) {
        if (!file.isEmpty()) {
            //文件名称
            int begin = Objects.requireNonNull(file.getOriginalFilename()).indexOf(".");
            //文件名称长度
            int last = file.getOriginalFilename().length();
            //判断文件格式是否正确
            String fileName = file.getOriginalFilename().substring(begin, last);
            if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
                throw new IllegalArgumentException("上传文件格式错误");
            }
        } else {
            throw new IllegalArgumentException("文件不能为空");
        }
        try (InputStream inputStream = file.getInputStream()) {
            if (type == 1){
                simpleRead(inputStream);
            }else if (type == 2){
                synchronousRead(inputStream);
            }else {
                repeatedRead(inputStream);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 最简单的读的监听器
     */
    public void simpleRead(InputStream inputStream){
        //获取正确数据
        ArrayList<GoodsImportExcel> successArrayList = new ArrayList<>();
        //获取错误数据
        ArrayList<GoodsImportExcel> errorArrayList = new ArrayList<>();
        EasyExcel.read(inputStream)
                .head(GoodsImportExcel.class)
                .registerReadListener(new DemoDataListener(
                        // 监听器中doAfterAllAnalysed执行此方法;所有读取完成之后处理逻辑
                        successArrayList::addAll, errorArrayList::addAll))
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题(字段列表)所在行数
                .headRowNumber(2)
                .doReadSync();
        System.out.println(successArrayList);
        System.out.println(errorArrayList);
    }

    /**
     * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
     */
    public void synchronousRead(InputStream inputStream){
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
        List<GoodsImportExcel> batchGoodsImportModels = EasyExcel.read(inputStream)
                .head(GoodsImportExcel.class)
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题(字段列表)所在行数
                .headRowNumber(2)
                .doReadSync();
        System.out.println(JSONObject.toJSONString(batchGoodsImportModels));
    }

    /**
     * 读取多个sheet
     */
    public void repeatedRead(InputStream inputStream){
        ArrayList<GoodsImportExcel> successArrayList = new ArrayList<>();
        //获取错误数据
        ArrayList<GoodsImportExcel> errorArrayList = new ArrayList<>();
        //使用模型来读取Excel(多个sheet)
        ExcelReader reader = EasyExcel.read(inputStream).build();
        //多个sheet
        List<ReadSheet> sheetList = new ArrayList<>();
        for (int i = 0; i < reader.getSheets().size(); i++){
            // 这里为了简单,所以注册了同样的head 和Listener 自己使用功能必须不同的Listener
            ReadSheet readSheet = EasyExcel.readSheet(i)
                    .head(GoodsImportExcel.class)
                    .registerReadListener(new DemoDataListener(successArrayList::addAll, errorArrayList::addAll))
                    // 设置标题(字段列表)所在行数
                    .headRowNumber(2)
                    .build();
            sheetList.add(readSheet);
        }
        // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
        reader.read(sheetList);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        reader.finish();
        System.out.println(successArrayList);
        System.out.println(errorArrayList);
    }
}

结果打印
[GoodsImportExcel(goodsName=苹果, price=10, num=11, errorMsg=null), GoodsImportExcel(goodsName=香蕉, price=8, num=12, errorMsg=null), GoodsImportExcel(goodsName=葡萄, price=20.0, num=40, errorMsg=null)]
[GoodsImportExcel(goodsName=梨子1111, price=11.0, num=30, errorMsg=2行的第1:商品名称长度超出5个字符串!)]

导入模版
JAVA实现easyExcel批量导入,项目技术框架,java,后端,excel

第三种:读取存在合并

监听器

package com.example.mybatismysql8demo.handler;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;



/**
 * Excel模板的读取监听类
 * @author gd
 */
public class ImportExcelListener<T> extends AnalysisEventListener<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImportExcelListener.class);

    /**
     * 解析的数据
     */
    private final List<T> list = new ArrayList<>();

    /**
     * 正文起始行
     */
    private final Integer headRowNumber;

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


    public ImportExcelListener(Integer headRowNumber) {
        this.headRowNumber = headRowNumber;
    }

    /**
     * 这个每一条数据解析都会来调用
     */
    @Override
    public void invoke(T data, AnalysisContext context) {
        LOGGER.info("数据处理: " + JSON.toJSONString(data));
        list.add(data);
    }

    /**
     * 所有数据解析完成了 都会来调用
     * @param context context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        LOGGER.info("所有数据解析完成!");
    }


    /**
     * 返回解析出来的List
     */
    public List<T> getData() {
        return list;
    }


    /**
     * 读取额外信息:合并单元格
     */
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        LOGGER.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
        switch (extra.getType()) {
            case COMMENT:
                LOGGER.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(), extra.getText());
                break;
            case MERGE: {
                LOGGER.info(
                        "额外信息是合并单元格,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
                        extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), extra.getLastColumnIndex());
                if (extra.getRowIndex() >= headRowNumber) {
                    extraMergeInfoList.add(extra);
                }
                break;
            }
            default:
        }
    }

    /**
     * 返回解析出来的合并单元格List
     */
    public List<CellExtra> getExtraMergeInfoList() {
        return extraMergeInfoList;
    }
}

合并数据处理工具类

package com.example.mybatismysql8demo.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.util.CollectionUtils;
import com.example.mybatismysql8demo.config.LengthValid;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.example.mybatismysql8demo.handler.ImportExcelListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;

public class ImportExcelMergeUtil<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImportExcelMergeUtil.class);

    /**
     * 返回解析后的List
     *
     * @param: fileName 文件名
     * @param: clazz Excel对应属性名
     * @param: sheetNo 要解析的sheet
     * @param: headRowNumber 正文起始行
     * @return java.util.List<T> 解析后的List
     */
    public void getList(InputStream inputStream, Class<GoodsImportExcel> clazz, Integer sheetNo, Integer headRowNumber,List<T> successList,List<T> errorList) {
        ImportExcelListener<T> listener = new ImportExcelListener<>(headRowNumber);
        try {
            EasyExcel.read(inputStream, clazz, listener).extraRead(CellExtraTypeEnum.MERGE).sheet(sheetNo).headRowNumber(headRowNumber).doRead();
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        List<CellExtra> extraMergeInfoList = listener.getExtraMergeInfoList();
        //解析数据
        List<T> list;
        if (CollectionUtils.isEmpty(extraMergeInfoList)) {
            list = (listener.getData());
        }else {
            list = explainMergeData(listener.getData(), extraMergeInfoList, headRowNumber);
        }
        //数据处理
        for (T v : list) {
            if(validParam(v)){
                errorList.add(v);
            }else {
                successList.add(v);
            }
        }
    }

    private Boolean validParam(T object){
        // 参数校验
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            //设置可访问
            field.setAccessible(true);
            //判断字段是否添加校验
            boolean valid = field.isAnnotationPresent(LengthValid.class);
            if (valid) {
                try {
                    //获取注解信息
                    LengthValid annotation = field.getAnnotation(LengthValid.class);
                    //值
                    String value = (String) field.get(object);
                    if(value.length() > annotation.length()){
                        //错误信息(需要设置字段为public)
                        Field errorMsg = object.getClass().getField("errorMsg");
                        if (errorMsg.get(object) == null){
                            errorMsg.set(object, annotation.msg());
                        }else {
                            errorMsg.set(object,errorMsg.get(object) + "," + annotation.msg());
                        }
                        return true;
                    }
                } catch (IllegalAccessException | NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

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

    /**
     * 设置合并单元格的值
     *
     * @param filedValue  值
     * @param rowIndex    行
     * @param columnIndex 列
     * @param data        解析数据
     */
    private void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<T> data) {
        T object = data.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) {
                        LOGGER.error("设置合并单元格的值异常:"+e.getMessage());
                    }
                }
            }
        }
    }


    /**
     * 获取合并单元格的初始值
     * rowIndex对应list的索引
     * columnIndex对应实体内的字段
     * @param firstRowIndex    起始行
     * @param firstColumnIndex 起始列
     * @param data             列数据
     * @return 初始值
     */
    private Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<T> data) {
        Object filedValue = null;
        T object = data.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) {
                        LOGGER.error("获取合并单元格的初始值异常:"+e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }
}

执行方法

package com.example.mybatismysql8demo.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.mybatismysql8demo.excel.GoodsImportExcel;
import com.example.mybatismysql8demo.utils.ImportExcelMergeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;


@Slf4j
@RestController
public class EasyExcelController {

    @PostMapping("/easyExcelImport")
    public void importExcel(MultipartFile file) {
        if (!file.isEmpty()) {
            //文件名称
            int begin = Objects.requireNonNull(file.getOriginalFilename()).indexOf(".");
            //文件名称长度
            int last = file.getOriginalFilename().length();
            //判断文件格式是否正确
            String fileName = file.getOriginalFilename().substring(begin, last);
            if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
                throw new IllegalArgumentException("上传文件格式错误");
            }
        } else {
            throw new IllegalArgumentException("文件不能为空");
        }
        ImportExcelMergeUtil<GoodsImportExcel> helper = new ImportExcelMergeUtil<>();
        List<GoodsImportExcel> successList = new ArrayList<>();
        List<GoodsImportExcel> errorList = new ArrayList<>();
        try {
            helper.getList(file.getInputStream(), GoodsImportExcel.class,0,2,successList,errorList);
            System.out.println("正确数据:"+successList);
            System.out.println("错误数据:"+errorList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

结果打印
正确数据:[GoodsImportExcel(goodsName=香蕉, price=8, num=12, errorMsg=null)]
错误数据:[GoodsImportExcel(goodsName=苹果11111, price=10, num=11, errorMsg=商品名称长度超出5个字符串!), GoodsImportExcel(goodsName=苹果11111, price=9.0, num=20, errorMsg=商品名称长度超出5个字符串!)]

导入模版
JAVA实现easyExcel批量导入,项目技术框架,java,后端,excel文章来源地址https://www.toymoban.com/news/detail-859882.html

到了这里,关于JAVA实现easyExcel批量导入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java批量导入Excel数据

    1.后台导入代码 2.实体类 2.1设置表格下拉选项  3.vue前端导入功能代码

    2024年02月09日
    浏览(39)
  • 基于Java SSM框架实现摄影器材租赁系统项目【项目源码+论文说明】

    基于java的SSM框架实现摄影器材租赁系统演示 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,

    2024年02月01日
    浏览(60)
  • 基于Java SSM框架实现体育竞赛成绩管理系统项目【项目源码+论文说明】

    基于java的SSM框架实现体育竞赛成绩管理系统演示 体育竞赛是各种体育体育项目比赛的总称。是在裁判员的主持下,按统一的规则要求,组织与实施的体育员个体或体育队之间的竞技较量,是竞技体育与社会发生关联,并作用于社会的媒介,随着网络市场的不断成熟,带动了

    2024年01月22日
    浏览(67)
  • 基于Java SSM框架实现线上教学平台系统项目【项目源码+论文说明】

    基于java的SSM框架实现线上教学平台演示 在社会快速发展的影响下,使线上教学平台的管理和运营比过去十年更加理性化。依照这一现实为基础,设计一个快捷而又方便的网上线上教学平台系统是一项十分重要并且有价值的事情。对于传统的线上教学平台控制模型来说,网上

    2024年01月20日
    浏览(57)
  • Java项目导入依赖(普通java项目)

    (1)创建lib文件夹 普通项目是没有lib文件夹的,可以在项目里面手动添加一个lib文件夹,和src文件夹同级 (2)下载jar包 !!这里的下载的jar包仅是示例,选择你自己要下载的包 apache官网(慢):http://ftp.cuhk.edu.hk/pub/packages/apache.org/ maven官网(推荐):http://www.mvnrepository.

    2024年02月11日
    浏览(40)
  • Java根据excel模版导出Excel(easyexcel、poi)——含项目测试例子拿来即用

    一般列表导出以及个性化样式设置请看下面的文章: JAVA导出Excel通用工具类——第一篇:详细介绍POI 导出excel的多种复杂情况,包括动态设置筛选、动态合并横向(纵向)单元格等多种复杂情况. JAVA导出Excel通用工具——第二篇:使用EasyExcel导出excel的多种情况的例子介绍.

    2024年04月29日
    浏览(40)
  • 基于Java SSM框架实现医院管理系统项目【项目源码】计算机毕业设计

    基于java的SSM框架实现医院管理系统演示 当今流行的“SSM组合框架”是Spring + SpringMVC + MyBatis的缩写,受到很多的追捧,“组合SSM框架”是强强联手、各司其职、协调互补的团队精神。web项目的框架,通常更简单的数据源。Spring属于一个轻量级的反转控制框架(IoC),但它也是一

    2024年02月01日
    浏览(77)
  • java实现excel的导出之使用easyExcel

    在我们的项目需求中,经常会遇到导出的需求,其中excel的导出最为常见。生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc. EasyExcel是阿里巴巴

    2024年02月15日
    浏览(46)
  • 基于Java SSM框架实现精准扶贫管理系统项目【项目源码】计算机毕业设计

    基于java的SSM框架实现精准扶贫管理系统演示 JSP技术本身是一种脚本语言,但它的功能是十分强大的,因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时,它可以使显示逻辑和内容分开,这就极大的方便了用户的需求。JavaBeans 可以对JSP技术的程序进行扩展,从而形成

    2024年02月19日
    浏览(53)
  • 基于Java SSM框架实现在线作业管理系统项目【项目源码】计算机毕业设计

    基于java的SSM框架实现在线作业管理系统演示 JSP技术本身是一种脚本语言,但它的功能是十分强大的,因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时,它可以使显示逻辑和内容分开,这就极大的方便了运动员的需求。JavaBeans 可以对JSP技术的程序进行扩展,从而形

    2024年01月20日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包