前瞻
项目中需要使用Excel表格来导入数据,基于easyexcel实现Excel表格进行数据导入
问题
由于导入表格的种类较多,使用的依赖过于频繁,使用该方法时,如果使用pio的依赖来实现下载excle表格时,会出现冲突的现象,原因是由于依赖冲突导致,所以在使用该方法之前,如果使用pio方法进行下载表格时,可能会发生异常
建议
在使用该方法进行导入时,可使用之前的一篇文档,基于easyexcel实现的Excel表格下载
链接:https://blog.csdn.net/ldy15729357137/article/details/139521745
下面进入正题,Excel导入
Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
实现类
由于导入的Excel表格中,表头信息和校验信息不相同,无法实现统一的方法,只能实现定制化的功能,针对功能进行实现,但基本的外壳是相同的,只不过业务逻辑有所不同
实体类
创建实体类,用于接收表格的信息
其中@ExcelProperty注解中的内容即为插入表格中表头的名称,要于实体类中的一一对应
package com.ponshine.applets.excle.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* @author LiDongYang
* @description:
* @date 2024/6/11 13:02
*/
@Data
public class ImportBachDto {
@ExcelProperty("编码")
private String id;
@ExcelProperty("名称")
private String name;
@ExcelProperty("类型")
private String type;
}
表格内容处理类
继承AnalysisEventListener类,实现对应的方法,执行顺序如下
1、invokeHeadMap: 读取第一行数据,表头信息,校验表头信息是否符合要求
2、invoke: 循环执行,一次读取一条数据进行校验
3、doAfterAllAnalysed: 当表格中所有的数据执行完之后,执行该方法,一般情况是时将数据进行插入
4、onException: 数据抛异常时的处理类
package com.ponshine.applets.excle.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.CellExtra;
import com.google.gson.Gson;
import com.ponshine.applets.excle.entity.ImportBachDto;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
/**
* @author Li DongYang
* @description:
* @date: 2023/4/13 15:25
*/
@Slf4j
public class DeviceImportListener extends AnalysisEventListener<ImportBachDto> {
/**
* Excle表头信息,这里做校验使用
*/
private static String[] COLUMN = new String[]{"编码","名称","类型"};
public DeviceImportListener() {
}
/**
* 所有数据解析完成了 都会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
log.info("所有数据解析完成!");
}
/**
* 读取时,每条数据都会从这里解析
*/
@Override
public void invoke(ImportBachDto data, AnalysisContext context) {
}
/**
* 读取表头数据
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
Gson gson = new Gson();
log.info("解析到一条头数据:{}", gson.toJson(headMap));
if (headMap.size() == COLUMN.length) {
for (int i = 0; i < headMap.size(); i++) {
if (!COLUMN[i].equals(headMap.get(i))) {
System.out.println("文件头部错误");
break;
}
}
} else {
System.out.println("文件头部错误");
}
}
/**
* 读取额外信息
*
* @param extra
* @param context
*/
@Override
public void extra(CellExtra extra, AnalysisContext context) {
Gson gson = new Gson();
log.info("读取到了一条额外信息:{}", gson.toJson(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 {
log.info("Unknown hyperlink!");
}
break;
case MERGE:
log.info(
"额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
extra.getLastColumnIndex());
break;
default:
}
}
/**
* 用日期去接字符串 肯定报错,此时需要用到异常处理
* 在转换异常获取其他异常下会调用本接口。
* 抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
*/
@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());
}
}
}
业务层调用
上面的工具类时对数据进行处理,下面的是如何调用
public String importBach(MultipartFile file) {
StringBuilder result = new StringBuilder();
try {
String name = file.getOriginalFilename();
//校验文件大小
if (file.getSize() > 10485760) {
System.out.println("上传文件过大,请上传10M以内的文件!");
}
if (StringUtils.isEmpty(name) || (!name.endsWith(".xlsx") && !name.endsWith(".xls"))) {
System.out.println("文件格式错误!");
}
//测站信息导入,先获取原测站基本信息
//获取字典配置信息(区域、流域、测站类型)
ImportListener siteImportListener = new ImportListener();
ExcelReaderBuilder read = EasyExcelFactory.read(file.getInputStream(), ImportBachDto.class, siteImportListener);
read.sheet().doRead();
Integer successCount = siteImportListener.getSuccessCount();
Integer errorCount = siteImportListener.getErrorCount();
Integer totalCount = successCount + errorCount;
result.append("批量导入结束,此次导入总量为 ").append(totalCount).append("条")
.append(", 其中成功 ").append(successCount).append("条, ").append("失败 ").append(errorCount).append("条");
} catch (Exception e) {
e.printStackTrace();
System.out.println("批量导入异常");
}
return result.toString();
}
到此到此的功能就结束了,针对不同的需求,可对代码进行优化调整