her~~llo,我是你们的好朋友Lyle,是名梦想成为计算机大佬的男人!
博客是为了记录自我的学习历程,加强记忆方便复习,如有不足之处还望多多包涵!非常欢迎大家的批评指正。
最近开发项目需要用到比较复杂的高级导入,于是就学习了EasyExcel的使用,现在总结一下。有什么新内容会持续更新。
目录
一、EasyExcelFactory工厂类
二、自定义EasyExcel工具类
三、监听器
四、实例演示
首先说一下版本吧,我使用的是2.2.7的版本。
com.alibaba easyexcel 2.2.7
首先我建议大家可以看一下EasyExcel为我们提供的工厂类EasyExcelFactory代码,其中经常用到的有这些:
读取文件导入的话就是
read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();写文件导出的话就是
write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);看懂源代码的话,对我们自己编写适合自己项目的方法很用用处。
实际开发中,我们在导入excel文件时有很多种情况,我在下方各种情况都列举出来了,其中展示了同步读取的情况的对应代码,因为太多了,不方便展示。有需要学习的小伙伴可以私信我。
/**
* EasyExcel工具类
*/
public class EasyExcelUtils {
/**
* 同步无模型读取(默认读取sheet0,从第2行开始读)
*
* @param filePath 文件路径
* @return List
监听器在我看来就是为了对读取的数据进行校验(空校验,类型校验),用于异步读取,我在上面展示了异步按模型读取文件的对应代码,其中有一个参数就是
AnalysisEventListenerexcelListener 这里需要我们,需要传一个AnalysisEventListener
进去,我们可以根据进行自己需求的扩展AnalysisEventListener这个类,AnalysisEventListener又继承了ReadListener,我们看一下ReadListener。其中有几个方法。几个方法的用处我把自己的理解敲了上去。
public interface ReadListener extends Listener {
// 在转换异常获取其他异常下会调用本接口。
void onException(Exception var1, AnalysisContext var2) throws Exception;
//读取表头数据存在headMap中
void invokeHead(Map var1, AnalysisContext var2);
//读取一行一行数据到var1
void invoke(T var1, AnalysisContext var2);
void extra(CellExtra var1, AnalysisContext var2);
//AOP思想,在完成数据解析后进行的操作
void doAfterAllAnalysed(AnalysisContext var1);
boolean hasNext(AnalysisContext var1);
}
在这里我提供一个自己写的代码,加深大家的理解。
/**
* 修改默认监听器,增加特殊需求
*
* @param 泛型
* @author Lyle
*/
public class ExcelListener extends AnalysisEventListener {
// 保存读取的对象
private final List rows = new ArrayList<>();
// 日志输出
private final Logger logger = LoggerFactory.getLogger(getClass());
private String sheetName = "";
// 获取对应类
private Class headClazz;
// 此map用来存储错误信息
private final List errorMessage = new ArrayList<>();
public ExcelListener(Class headClazz) {
this.headClazz = headClazz;
}
/**
* @param headClazz
* @Description 通过class获取类字段信息
*/
public Map getIndexNameMap(Class headClazz) throws NoSuchFieldException {
Map result = new HashMap<>();
Field field;
Field[] fields = headClazz.getDeclaredFields(); //获取类中所有的属性
for (int i = 0; i < fields.length; i++) {
field = headClazz.getDeclaredField(fields[i].getName());
// logger.info(String.valueOf(field));
field.setAccessible(true);
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);//获取根据注解的方式获取ExcelProperty修饰的字段
if (excelProperty != null) {
int index = excelProperty.index(); //索引值
String[] values = excelProperty.value(); //字段值
StringBuilder value = new StringBuilder();
for (String v : values) {
value.append(v);
}
result.put(index, value.toString());
}
}
return result;
}
@Override
public void invokeHeadMap(Map headMap, AnalysisContext context) {
logger.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
Map head = new HashMap<>();
try {
head = getIndexNameMap(headClazz); //通过class获取到使用@ExcelProperty注解配置的字段
logger.info(String.valueOf(head));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
Set keySet = head.keySet(); //解析到的excel表头和实体配置的进行比对
for (Integer key : keySet) {
if (StringUtils.isEmpty(headMap.get(key))) {
errorMessage.add("您上传的文件第" + (key + 1) + "列表头为空,请安照模板检查后重新上传");
}
if (!headMap.get(key).equals(head.get(key))) {
errorMessage.add("您上传的文件第" + (key + 1) + "列表头与模板表头不一致,请检查后重新上传");
}
}
}
@Override
public void invoke(T object, AnalysisContext context) {
// 实际数据量比较大时,rows里的数据可以存到一定量之后进行批量处理(比如存到数据库),
// 然后清空列表,以防止内存占用过多造成OOM
rows.add(object);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 当前sheet的名称 编码获取类似
sheetName = context.readSheetHolder().getSheetName();
logger.info(sheetName);
logger.info("read {} rows", rows.size());
}
/**
* 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
*
* @param exception 抛出异常
* @param context 解析内容
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
logger.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
errorMessage.add("第" + excelDataConvertException.getRowIndex() + "行,第" + (excelDataConvertException.getColumnIndex() + 1) + "列数据类型解析异常,数据为:" + excelDataConvertException.getCellData());
logger.error("第{}行,第{}列数据类型解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex() + 1, excelDataConvertException.getCellData());
}
}
public List getRows() {
return rows;
}
public Class getHeadClazz() {
return headClazz;
}
public List getErrorMessage() {
return errorMessage;
}
public String getSheetName() {
return sheetName;
}
}
首先我们需要一个模板类TestDemo,也就是我们在数据库存取的实体类。
package com.swpu.component.commons.utils;
import com.alibaba.excel.annotation.ExcelProperty;
public class TestDemo {
@ExcelProperty(value = "地层压力",index = 0)
private float diCengYali;
@ExcelProperty(value = "破裂压力",index = 1)
private float poLieYaLi;
@ExcelProperty(value = "井径扩大率",index = 2)
private float jingJingKuoDa;
@Override
public String toString() {
return "TestDemo{" +
"diCengYali=" + diCengYali +
", poLieYaLi=" + poLieYaLi +
", jingJingKuoDa=" + jingJingKuoDa +
'}';
}
public float getDiCengYali() {
return diCengYali;
}
public void setDiCengYali(float diCengYali) {
this.diCengYali = diCengYali;
}
public float getPoLieYaLi() {
return poLieYaLi;
}
public void setPoLieYaLi(float poLieYaLi) {
this.poLieYaLi = poLieYaLi;
}
public float getJingJingKuoDa() {
return jingJingKuoDa;
}
public void setJingJingKuoDa(float jingJingKuoDa) {
this.jingJingKuoDa = jingJingKuoDa;
}
}
测试运行代码:
public class ExcelUtilsTest {
@Test
@DisplayName("测试Excel导入")
void testExcel() throws IOException {
//待解析的文件路径
File file = new File("C:\\Users\\Administrator\\Desktop\\测试.xlsx");
//声明监听器
ExcelListener myListener=new ExcelListener(TestDemo.class);
//调用EasyExcelUtils
EasyExcelUtils.asyncReadModel(file, myListener,TestDemo.class,0, 1);
//获取解决出的错误信息
List errorMessage = myListener.getErrorMessage();
System.out.println(errorMessage);
//获取监听器读到的数据,拿到的数据大家可以根据需求进行数据库操作
List rows = myListener.getRows();
for (Object testDemo:rows){
System.out.println(testDemo.toString());
}
}
}
运行结果图:
结语:
对于一键导入Excel这一功能,我刚开始感觉确实很难实现,EasyExcel可以帮我们解决了很多复杂的if判断,理解EasyExcel的实现,灵活使用EasyExcel可以让我们的开发效率提升数倍,大家有什么不理解的可以私信,或者在下方评论里留言。我也是小白,更深入的我还是不够了解。大家可以一起交流!