目录
实现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}{咱们来日方长,有缘江湖再见,告辞!}
咱们来日方长,有缘江湖再见,告辞!