EasyExcel 读取excel文件 java-demo

本文档介绍了一个使用EasyExcel处理Excel数据时遇到的空指针异常问题,问题源于在监听器中通过注解注入的HttpServletRequest为null。为了解决这个问题,作者提出了创建一个处理类,将需要注入的对象放入处理类中,并在监听器中传入处理类的实例。改造后的监听器、serviceImpl和处理类的代码示例分别进行了展示,实现了在读取Excel过程中进行数据校验和存储。
摘要由CSDN通过智能技术生成

在这里插入图片描述

实现demo

配置maven依赖

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>easyexcel</artifactId>
   <version>2.2.6</version>
 </dependency>

java代码(埋了个小坑)

1. Demodata类

作用可看作dto

@Data
public class DemoData{
    
    // index是表格的下标 从0开始
    @ExcelProperty(value = "名称",index = 0)
    private String name;

    @ExcelProperty(value = "性别",index = 1)
    private String sex;

    @ExcelProperty(value = "年龄",index = 2)
    private Integer age;
}

2. 表格的表头枚举类

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum ImportColumnEnum {

    NAME("*名称"),
    SEX("*性别"),
    AGE("*年龄");

    private String headerName;

    ImportColumnEnum(String headerName) {
        this.headerName = headerName;
    }

    public static List<String> headerColumnEnums(){
        ImportColumnEnum[] values = ImportColumnEnum.values();
        return Stream.of(values).map(headColumn -> {
            return headColumn.headerName;
        }).collect(Collectors.toList());
    }
}

3. 配置EasyExcel监听器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.entity.DemoData;
import com.example.demo.entity.ImportColumnEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class ExcelListener extends AnalysisEventListener<DemoData> {

    @Autowired
    private HttpServletRequest request;

    /**
     *
     * @description       一行一行读取excel内容 可校验单行数据
     * @date 2021/5/14 16:13
     * @param demoData
     * @param analysisContext
     */
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        // 这里的行数没有包括标题,所以这里需要加1
        Integer currentRowNum = analysisContext.readRowHolder().getRowIndex() + 1;
        log.info("行号:{},单行数据:{}",currentRowNum,demoData);
    }

    /**
     *
     * @description       读取表头内容  在这可以对表头内容进行校验
     * @date 2021/5/14 16:11
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //校验表头 是否正确
        List<String> headerList = headMap.values().stream().collect(Collectors.toList());

        List<String> listHeadColumnEnum = ImportColumnEnum.headerColumnEnums();
        Stream.iterate(0, i -> i + 1).limit(listHeadColumnEnum.size()).forEach(index -> {
            String titleColumnName = listHeadColumnEnum.get(index);
            String excelColumnName = headerList.get(index);
            if (!titleColumnName.equalsIgnoreCase(excelColumnName)) {
                log.info("表头格式错误!请导入正确表格");
            }
        });
    }

    /**
     *
     * @description       读取校验完数据 在这里可以进行存储
     * @date 2021/5/14 16:11
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //存储数据
        String id = request.getSession().getId();
        log.info("session:{}",id);
    }

}

4. service

public interface DemoService {

    boolean add(MultipartFile file);

}

5. serviceImpl

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.example.demo.config.excel.ExcelListener;
import com.example.demo.entity.DemoData;
import com.example.demo.services.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


@Service
@Slf4j
public class DemoServiceImpl implements DemoService {
    @Override
    public boolean add(MultipartFile file) {
        try{
            EasyExcel.read(file.getInputStream(), DemoData.class, new ExcelListener())
                    //表格类型
                    .excelType(ExcelTypeEnum.XLSX)
                    //自动关闭流
                    .autoCloseStream(true)
                    //忽略空行
                    .ignoreEmptyRow(true)
                    //去两端空格
                    .autoTrim(true)
                    //读取第一个sheet
                    .sheet(0).doRead();
        } catch (Exception e) {
            log.info(e.getMessage(),e);
            e.printStackTrace();
        }
        return true;
    }
}

6. controller接收的dto

/**
 *
 * @description
 * @date 2021/5/14 16:24
 */
@Data
public class AddsDto {
    @ApiModelProperty(value = "模板文件")
    private MultipartFile file;
}

7. controller

 @Autowired
    DemoService demoService;

    @PostMapping(value = "/import",consumes = {"multipart/form-data"})
    public boolean add(AddsDto addsDto){
        return demoService.add(addsDto.getFile());
    }

8. postman调用

在这里插入图片描述

9. 效果图

在这里插入图片描述

为什么会有空指针呢?原来通过注解@Autowired或@Resource注入bean时,获取到的bean为null。调用该bean的方法时会报空指针异常。

当调用bean内方法时,spring容器中还没有完成对注解bean的扫描,dispatcher.xml中配置的注解bean的优先级没有框架中的contextListener的优先级高,所以contextListener初始化的时候根据@Autowired扫描,肯定是null的。

不需要用到注入的类的话,把request那部分内容去掉就能读取到excel了~ 也就不会有这个问题,不过一般都是需要处理逻辑需要注入~

-------------------------------引用另一博主的观点

怎么解决?网上挺多资料的,但是本人测试都没走通,那么另走通道,我在serviceimpl调用的时候提前将处理类给注入进去,然后再传入到监听器中,就走得通了,那么跟着我来改造~~

怎么解决坑

新建处理类,把有关需要注入的类都放处理类,再把处理类传入到监听器中~

1. 处理类

import com.example.demo.entity.DemoData;
import com.example.demo.entity.ImportColumnEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
@Slf4j
public class ImportProcessor {

    @Autowired
    private HttpServletRequest request;

    /**
     * @param headerList
     * @author liqian
     * @description excel头部校验
     * @date 2021/5/13 10:54
     */
    public void checkHead(List<String> headerList) {
        //校验导入excel表头是否与设置的模板一样,如果不一样一般给出错误提示 终止下一步读取excel内容动作
        List<String> listHeadColumnEnum = ImportColumnEnum.headerColumnEnums();
        Stream.iterate(0, i -> i + 1).limit(listHeadColumnEnum.size()).forEach(index -> {
            String titleColumnName = listHeadColumnEnum.get(index);
            String excelColumnName = headerList.get(index);
            if (!titleColumnName.equalsIgnoreCase(excelColumnName)) {
                log.info("表头格式错误!请导入正确表格");
            }
        });
    }

    /**
     *
     * @author liqian
     * @description
     * @date 2021/5/14 16:01
     * @param demoData 校验的当行数据
     * @param currentRowNum 行号
     * @param demoDataList 已校验成功的之前表格内数据
     */
    public void checkParam(DemoData demoData, Integer currentRowNum, List<DemoData> demoDataList) {
        //校验单行内容是否符合标准...........
        if (demoData.getName() == null){
            log.info("姓名必填!");
        }
        //校验姓名是否重复
        List<String> collect = demoDataList.stream().map(DemoData::getName).collect(Collectors.toList());
        if (collect.contains(demoData.getName())){
            log.info("第:{}行姓名重复!",currentRowNum);
        }
    }

    /**
     *
     * @author liqian
     * @description       单行校验都完成后进行数据库对比以及对比完成存入库
     * @date 2021/5/14 16:00
     * @param demoDataList
     */
    public void saveData(List<DemoData> demoDataList) {
        //测试是否能接收spring管理的bean 如果request能接收到值,那么后续注入的mapper也能接收 就可继续后续业务操作
        String sessionId = request.getSession().getId();
        log.info("sessionId:{}",sessionId);
        log.info("demoDataList:{}",demoDataList);
        //   后续业务操作.............
    }

}

2. 改造后的监听器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.entity.DemoData;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *
 * @author liqian
 * @description      监听器执行流程:读取表头 invokeHeadMap()--》读取表格内容 invoke()--》读取后操作doAfterAllAnalysed()  如果表格内容没内容不会执行 invoke方法读取内容
 * @date 2021/5/14 17:13
 */
@Slf4j
public class ExcelListener extends AnalysisEventListener<DemoData> {


    ImportProcessor importProcessor;

    /**
     *
     * @author liqian
     * @description       单行内容校验完成存入进去
     * @date 2021/5/14 17:10
     */

    List<DemoData> demoDataList = new LinkedList<>();

    public ExcelListener(ImportProcessor importProcessor) {
        if (importProcessor == null) {
            log.info("数据操作对象不能为空");
        }
        this.importProcessor = importProcessor;
    }

    /**
     *
     * @description       一行一行读取excel内容 可校验单行数据
     * @date 2021/5/14 16:13
     * @param demoData
     * @param analysisContext
     */
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        // 这里的行数没有包括标题,所以这里需要加1
        Integer currentRowNum = analysisContext.readRowHolder().getRowIndex() + 1;
        log.info("行号:{},单行数据:{}",currentRowNum,demoData);
        importProcessor.checkParam(demoData,currentRowNum,demoDataList);
        //校验完成存入list
        demoDataList.add(demoData);
    }

    /**
     *
     * @description       读取表头内容  在这可以对表头内容进行校验
     * @date 2021/5/14 16:11
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //校验表头 是否正确
        List<String> headerList = headMap.values().stream().collect(Collectors.toList());
        importProcessor.checkHead(headerList);
    }

    /**
     *
     * @description       读取校验完数据 在这里可以进行存储
     * @date 2021/5/14 16:11
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //存储数据
        importProcessor.saveData(demoDataList);
    }

}

3. 改造后的serviceImpl

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.example.demo.config.excel.ExcelListener;
import com.example.demo.config.excel.ImportProcessor;
import com.example.demo.entity.DemoData;
import com.example.demo.services.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


@Service
@Slf4j
public class DemoServiceImpl implements DemoService {

    @Autowired
    private ImportProcessor importProcessor;

    @Override
    public boolean add(MultipartFile file) {

        try{
            ExcelListener readListener = new ExcelListener(importProcessor);
            EasyExcel.read(file.getInputStream(), DemoData.class, readListener)
                    //表格类型
                    .excelType(ExcelTypeEnum.XLSX)
                    //自动关闭流
                    .autoCloseStream(true)
                    //忽略空行
                    .ignoreEmptyRow(true)
                    //去两端空格
                    .autoTrim(true)
                    //读取第一个sheet
                    .sheet(0).doRead();
        } catch (Exception e) {
            log.info(e.getMessage(),e);
            e.printStackTrace();
        }
        return true;
    }
}

4. 效果图

postman还是老样子,不重复,直接上控制台打印的sessionId
在这里插入图片描述

就先说到这 \color{#008B8B}{ 就先说到这} 就先说到这
在下 A p o l l o \color{#008B8B}{在下Apollo} 在下Apollo
一个爱分享 J a v a 、生活的小人物, \color{#008B8B}{一个爱分享Java、生活的小人物,} 一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞! \color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!} 咱们来日方长,有缘江湖再见,告辞!
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值