springboot项目利用easypoi导入导出(包括一对多导出的动态列选择,以及有错误数据导入返回错误信息)
因为项目只涉及到一对多的导出,以及普通的导入,所以,本文只会涉及这方面的使用
导入的时候,有校验,如果有错误数据,就会返回错误数据的所在行,以及错误信息(如果需要返回错误信息的所在的那几行数据以及错误信息的excel文件的话,可以看看第三个参考文章,这个项目最开始是做的返回excel文件,最后又取消了)
参考了一下文章(排名不分先后):
- easypoi的官方文档
- 使用easypoi根据表头信息动态导出
- 微服务中EasyPoi上传Excel文件带校验并返回错误信息
- easyPOI基本用法
- Springboot 导入导出Excel ,一对多关系,复合表格、合并单元格数据
- SpringBoot实现Excel导入导出,好用到爆,POI可以扔掉了!
一. 前置准备
1. 在pom.xml中导入依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
2. application.yml配置环境
# mybatis yml文件配置
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
# 映射数据库中的下划线命名到 java 中的驼峰命名
map-underscore-to-camel-case: true
3. 工具包
MyExcelUtils.java
package com.xxx.template.utils.recruitmentManagement;
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;
import com.byd.template.log.Log;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MyExcelUtils {
/**
* 修改fields上@Excel注解的name属性,不需要下载的列,name修改增加_ignore.
* 保存原来的@Excel注解name属性值,本次生成后用来恢复
* @Params
* headers:用户勾选,由前端传来的列名,列名的key必须和 Model字段对应
* clazz:model实体类
* excelMap:用来记录原值的map,因为用到了递归,这里返回值作为参数传入
* @return Map<String, String> 原实体类字段名和 @Excel注解 name属性值的映射关系<字段名,@Excel注解 name 属性值>
*/
public static Map<String, String> dynamicChangeAndSaveSourceAnnotation(List<String> headers, Class clazz, Map<String, String> excelMap) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// @Excel注解
if (field.isAnnotationPresent(Excel.class)) {
boolean flag = true;
if(headers == null || headers.size()==0){
flag =true;
}else{
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i);
if (field.getName().equals(header)) {
flag = false;
break;
}
}
}
// 下载列不包括该字段,进行隐藏,并记录原始值
if (flag) {
Excel annotation = field.getAnnotation(Excel.class);
// 保存注解
excelMap.put(field.getName(), annotation.name());
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
String value = annotation.name().toString();
changeAnnotationValue(handler, field.getName() + "_ignore");
}
// @ExcelCollection注解
} else if (field.isAnnotationPresent(ExcelCollection.class) && field.getType().isAssignableFrom(List.class)) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Class collectionClazz = (Class) pt.getActualTypeArguments()[0];
// 解决@ExcelCollection如果没有需要下载列的异常,java.lang.IllegalArgumentException: The 'to' col (15) must not be less than the 'from' col (16)
// 如果没有需要下载列,将@ExcelCollection忽略
Field[] collectionFields = collectionClazz.getDeclaredFields();
boolean flag = false;
out:
for (Field temp : collectionFields) {
if (!temp.isAnnotationPresent(Excel.class)) {
continue;
}
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i);
if (temp.getName().equals(header)) {
flag = true;
break out;
}
}
}
if (flag) {
dynamicChangeAndSaveSourceAnnotation(headers, collectionClazz, excelMap);
} else {
ExcelCollection annotation = field.getAnnotation(ExcelCollection.class);
excelMap.put(field.getName(), annotation.name());
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
changeAnnotationValue(handler, field.getName() + "_ignore");
}
}
// @ExcelEntity注解
} else if (field.isAnnotationPresent(ExcelEntity.class)) {
Class entityClazz = field.getType();
dynamicChangeAndSaveSourceAnnotation(headers, entityClazz, excelMap);
}
}
return excelMap;
}
public static Map<String, String> dynamicChangeAndSaveSourceAnnotation(List<String> headers, List<String> otherHeaders,Class clazz, Map<String, String> excelMap) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// @Excel注解
if ( field.isAnnotationPresent(Excel.class)) {
boolean flag = true;
if(headers == null || headers.size()==0){
flag =true;
}else{
for (int i = 0; i < headers.size(); i++) {