EasyExcel详解(结合官方文档)

EasyExcel

零、前言

文章是根据官方文档,加上自己的测试运行总结出来的,目前只总结的EasyExcel读的部分,写的部分还未完结,后续会更新

1、官方文档

https://easyexcel.opensource.alibaba.com/

2、EasyExcel的maven依赖

<!--Easy Excel-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version>
</dependency>

一、具体使用演示&说明

1、读Excel

1.1 简单的读

创建一个读取对象,存储读取的内容

/*** 读对象* @author banana* @create 2023-12-26 11:39*/@Data  //生成getter、setter、toString、equals、hashCode等方法
public class ReadDemoData {private String string;private Date date;private Double doubleData;
}

创建一个读的监听器

package com.example.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.example.model.pojo.ReadDemoData;
import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Map;/**** @author banana* @create 2023-12-26 11:50*/@Slf4j //Lombok 注解之一,它可以帮助我们在 Java 类中自动添加日志记录功能
public class ReadDemoDataListener implements ReadListener<ReadDemoData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<ReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(ReadDemoData readDemoData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(readDemoData));cachedDataList.add(readDemoData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return false;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

由于我们之后的测试都要在单元测试中进行,因此我们在单元测试目录下创建一个resources目录,用于后面对excel文件的读取

创建一个excel文件,名称为readExcel

在这里插入图片描述

excel文件中共有四百十条数据
在这里插入图片描述

在单元测试中创建一个方法,专门根据名称去读取单元测试目录中resources目录下保存的excel文件

//根据文件名称,获取Excel目录地址
private String getExcelUrl(String docName){return this.getClass().getClassLoader().getResource("").getPath()  + docName;
}

之后创建一个单元测试类,去进行简单的读取操作

① 方法一:JDK8+ ,不用额外写一个DemoDataListener

实现代码:

//写法一
String fileName =  getExcelUrl("readExcel.xlsx");
// 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
// 具体需要返回多少行可以在`PageReadListener`的构造函数设置
EasyExcel.read(fileName, ReadDemoData.class, new PageReadListener<ReadDemoData>(dataList ->{for(ReadDemoData demoData : dataList){log.info("读取到一条数据{}", JSON.toJSONString(demoData));}
})).sheet().doRead();

具体运行结果:

在这里插入图片描述

我们可以看下PageReadListener源码,其就是继承ReadListener接口的一个实现类,相当于是EasyExcel帮我们写好的读的监听器,我们直接拿来用就好了

public class PageReadListener<T> implements ReadListener<T> {public static int BATCH_COUNT = 100;private List<T> cachedDataList;private final Consumer<List<T>> consumer;private final int batchCount;public PageReadListener(Consumer<List<T>> consumer) {this(consumer, BATCH_COUNT);}public PageReadListener(Consumer<List<T>> consumer, int batchCount) {this.cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);this.consumer = consumer;this.batchCount = batchCount;}public void invoke(T data, AnalysisContext context) {this.cachedDataList.add(data);if (this.cachedDataList.size() >= this.batchCount) {this.consumer.accept(this.cachedDataList);this.cachedDataList = ListUtils.newArrayListWithExpectedSize(this.batchCount);}}public void doAfterAllAnalysed(AnalysisContext context) {if (CollectionUtils.isNotEmpty(this.cachedDataList)) {this.consumer.accept(this.cachedDataList);}}
}

并且其每次读取100条数据,然后返回过来,执行监听器中的方法。如果我们需要具体指定需要返回多少行,可以调用PageReadListener的构造器进行设置,如下所示:

调用函数PageReadListener构造器,指定batchCount的值:

public PageReadListener(Consumer<List<T>> consumer, int batchCount) {this.cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);this.consumer = consumer;this.batchCount = batchCount;}

完整调用构造器方式如下

//写法一
String fileName =  getExcelUrl("readExcel.xlsx");
// 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
// 具体需要返回多少行可以在`PageReadListener`的构造函数设置
EasyExcel.read(fileName, ReadDemoData.class, new PageReadListener<ReadDemoData>(dataList ->{for(ReadDemoData demoData : dataList){log.info("读取到一条数据{}", JSON.toJSONString(demoData));}
}, 20)).sheet().doRead();

每次到达监听器时的数量(指定batchCount前):

在这里插入图片描述

每次到达监听器时的数量(指定batchCount后):

在这里插入图片描述

② 方法二:匿名内部类 不用额外写一个DemoDataListener

其实质上是和方法一一样,只不过将监听器写成了内部类的方式去实现

代码实现:

String fileName = getExcelUrl("readExcel.xlsx");
EasyExcel.read(fileName, ReadDemoData.class, new ReadListener<ReadDemoData>() {//单次缓存数据量public static final int BATCH_COUNT = 100;//临时存储private List<ReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(ReadDemoData readDemoData, AnalysisContext analysisContext) {cachedDataList.add(readDemoData);if(cachedDataList.size() >= BATCH_COUNT){saveData();//存储完成,清理listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {saveData();}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}).sheet().doRead();

运行结果:

在这里插入图片描述

③ 方式三:使用自定义的ReadListener的实现类

代码实现:

//写法三
String fileName = getExcelUrl("readExcel.xlsx");
//有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, ReadDemoData.class, new ReadDemoDataListener()).sheet().doRead();

ReadDemoDataListener

@Slf4j //Lombok 注解之一,它可以帮助我们在 Java 类中自动添加日志记录功能
public class ReadDemoDataListener implements ReadListener<ReadDemoData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<ReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(ReadDemoData readDemoData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(readDemoData));cachedDataList.add(readDemoData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

运行结果:

在这里插入图片描述

④ 方法四:使用 build 方法构建 ExcelReader 对象

使用了try-with-resources 结构,通过使用 try-with-resources 结构,可以确保在代码块执行完毕或者出现异常时,会自动关闭在 try 括号中声明的资源,下面的ExcelReader 对象属于一个需要手动关闭的资源,try-with-resources 可以确保在读取操作结束后会自动关闭 ExcelReader,释放相关资源,无需手动编写关闭资源的代码。

try-with-resources结构如下:

// ResourceType 是要操作的资源类型
// initialization 是初始化资源的表达式。
//在 try 块中使用资源的代码块执行完毕后,会自动调用资源的 close() 方法进行关闭
try (ResourceType resource = initialization) {// 使用资源的代码块
} catch (ExceptionType exception) {// 异常处理代码块
}

代码实现:

//写法四
String fileName = getExcelUrl("readExcel.xlsx");
// 一个文件一个reader
try (ExcelReader excelReader = EasyExcel.read(fileName, ReadDemoData.class, new ReadDemoDataListener()).build()) {// 构建一个sheet 这里可以指定名字或者noReadSheet readSheet = EasyExcel.readSheet(0).build();// 读取一个sheetexcelReader.read(readSheet);
}

运行结果:

在这里插入图片描述

1.2 指定列的下标或者列名

① 创建读对象:

/** 指定列的下标或者列名的读对象* @author banana* @create 2023-12-26 21:00*/
@Data
public class IndexOrNameData {/*** 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配*/@ExcelProperty(index = 2)private Double doubleData;/*** 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据*/@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;
}

@ExcelProperty注解说明:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelProperty {//字符串数组类型,默认值为 ""。用于指定 Excel 中的列名,可以指定多个列名,用于匹配 Excel 文件中的列,用于读取或写入 Excel 数据时的映射关系String[] value() default {""};//整型变量,默认值为 -1。用于指定 Excel 中的列索引,用于读取或写入 Excel 数据时的映射关系int index() default -1;//整型变量,默认值为 2147483647。用于指定 Excel 中的列顺序,表示在 Excel 文件中的列的位置int order() default 2147483647;//泛型类型为 Converter 的 Class,默认值为 AutoConverter.class。用于指定转换器,用于在读取或写入 Excel 数据时进行数据类型的转换Class<? extends Converter<?>> converter() default AutoConverter.class;//原来用于指定格式化字符串,用于读取或写入 Excel 数据时的格式化(过时)/** @deprecated */@DeprecatedString format() default "";
}

②创建一个监听器(使用1.1中的监听器即可)

/*** @author banana* @create 2023-12-26 21:14*/
@Slf4j
public class IndexOrNameDataListener implements ReadListener<IndexOrNameData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<IndexOrNameData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(IndexOrNameData indexOrNameData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(indexOrNameData));cachedDataList.add(indexOrNameData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

③单元测试代码

/*** 指定列的下标或者列名** <p>1. 创建excel对应的实体对象,并使用 ExcelProperty注解. 参照IndexOrNameData* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照 IndexOrNameDataListener* <p>3. 直接读即可
*/
@Test
public void indexOrNameRead() {String fileName = getExcelUrl("readExcel.xlsx");// 这里默认读取第一个sheetEasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
}//根据文件名称,获取Excel目录地址
private String getExcelUrl(String docName){return this.getClass().getClassLoader().getResource("").getPath()  + docName;
}

④运行结果

在这里插入图片描述

1.3 读多个sheet

①创建Excel表

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

② 创建对象

这里使用1.1的对象ReadDemoData

@Data  //生成getter、setter、toString、equals、hashCode等方法
public class ReadDemoData {private String string;private Date date;private Double doubleData;
}

③创建监听器

使用1.1的监听器ReadDemoDataListener

/**** @author banana* @create 2023-12-26 11:50*/@Slf4j //Lombok 注解之一,它可以帮助我们在 Java 类中自动添加日志记录功能
public class ReadDemoDataListener implements ReadListener<ReadDemoData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<ReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(ReadDemoData readDemoData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(readDemoData));cachedDataList.add(readDemoData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

④单元测试

方法一:通过doReadAll读取全部sheet

/*** 3 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件* <p>* 1. 创建excel对应的实体对象 参照ReadDemoData* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照ReadDemoDataListener* <p>* 3. 直接读即可*/@Testpublic void repeateRead(){//写法一:读取全部sheet//获取Excel表的路径String fileName = getExcelUrl("MultipleSheetExcel.xlsx");// 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。// 然后所有sheet都会往同一个DemoDataListener里面写EasyExcel.read(fileName, ReadDemoData.class, new ReadDemoDataListener()).doReadAll();}

运行结果:

前面是通过监听器的invoke方法中的log.info("解析到一条数据:{}", JSON.toJSONString(readDemoData));

后面是通过模拟存储数据库中的saveData方法打印出来的

关于后面为什么把Sheet1和Sheet2的内容全部打印出来,是因为设置的BATCH_COUNT是100,没有清楚过缓存,所以执行saveData方法的时候都打印出来了

在这里插入图片描述

方法二:通过doReadAll读取全部sheet

/*** 3 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件* <p>* 1. 创建excel对应的实体对象 参照ReadDemoData* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照ReadDemoDataListener* <p>* 3. 直接读即可*/
@Test
public void repeateRead(){//写法二:分开读取各个Sheet的信息String fileName = getExcelUrl("MultipleSheetExcel.xlsx");try (ExcelReader excelReader = EasyExcel.read(fileName).build()) {// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的ListenerReadSheet readSheet1 =EasyExcel.readSheet(0).head(ReadDemoData.class).registerReadListener(new ReadDemoDataListener()).build();ReadSheet readSheet2 =EasyExcel.readSheet(1).head(ReadDemoData.class).registerReadListener(new ReadDemoDataListener()).build();// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能excelReader.read(readSheet1, readSheet2);}}

运行结果:

在这里插入图片描述

1.4 日期、数字或者自定义格式转换

① 创建自定义类型转换器

package com.example.converter;import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;/** 自定义转换器* @author banana* @create 2023-12-26 23:15*/
//将自定义转换器基础EasyExcel提供的转换接口Converter
public class CustomStringStringConverter implements Converter<String> {//指定该转换器支持的 Java 类型,这里指定为 String.class@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}//指定该转换器支持的 Excel 数据类型,这里指定为 CellDataTypeEnum.STRING,表示支持读取 Excel 中的字符串类型数据/*以下是 CellDataTypeEnum 中定义的所有单元格类型:- BOOL:布尔类型- ERROR:错误类型- FORMULA:公式类型- INLINE_STR:内联字符串类型- NUMBER:数字类型- STRING:字符串类型- DATE:日期类型- EMPTY:空类型*/@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 将读取到的 Excel 数据转换为 Java 对象中的数据* 这里读的时候会调用** @param context* @return*/@Overridepublic String convertToJavaData(ReadConverterContext<?> context) {return "自定义:" + context.getReadCellData().getStringValue();}/*** 用于将 Java 对象中的数据转换为写入 Excel 文件中的数据* 这里是写的时候会调用 不用管** @return*/@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {return new WriteCellData<>(context.getValue());}}

② 创建对象

/** 自定义格式转换对象* @author banana* @create 2023-12-26 23:13*/
@Data
public class ConverterData {//我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”@ExcelProperty(converter = CustomStringStringConverter.class)private String string;//这里用string 去接日期才能格式化。我想接收年月日格式@DateTimeFormat(pattern = "yyyy年MM月dd日HH时mm分ss秒")private String date;//我想接收百分比的数字@NumberFormat("#.##%")private String doubleData;
}

③ 自定义格式转换监听器

/*** 自定义格式转换监听器* @author banana* @create 2023-12-26 23:29*/
@Slf4j
public class ConverterDataListener implements ReadListener<ConverterData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<ConverterData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(ConverterData converterData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(converterData));cachedDataList.add(converterData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

④设置excel单元格格式

右击单元格,选择设置单元格格式

在这里插入图片描述

将日期标题的第一个单元格内容格式改成日期

在这里插入图片描述

将数字标题中第一个单元格内容改成数值

在这里插入图片描述

⑤单元测试

 /*** 4、日期、数字或者自定义格式转换* <p>* 默认读的转换器DefaultConverterLoader、loadDefaultReadConverter()* <p>1. 创建excel对应的实体对象 参照ConverterData.里面可以使用注解DateTimeFormat、NumberFormat或者自定义注解* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照ConverterDataListener* <p>3. 直接读即可*/
@Test
public void converterRead() {String fileName = getExcelUrl("readExcel.xlsx");// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())// 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。// 如果就想单个字段使用请使用@ExcelProperty 指定converter// .registerConverter(new CustomStringStringConverter())// 读取sheet.sheet().doRead();
}

运行结果:

在这里插入图片描述

1.5 日期、数字或者自定义格式转换
1.6 日期、数字或者自定义格式转换
1.7 读取表头数据

①监听器中重写invokeHeadMap方法方法

/***	读取表头数据监听器* @author banana* @create 2023-12-26 23:49*/
@Slf4j
public class DemoHeadDataListener implements ReadListener<ReadDemoData> {//每隔100条存储数据库,然后清理list,方便内存的回收private static final int BATCH_COUNT = 100;//缓存数据private List<ReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);//在解析 Excel 过程中发生异常时调用的方法。可以在该方法中记录日志或者进行异常处理等操作@Overridepublic void onException(Exception e, AnalysisContext analysisContext) throws Exception {}//在读取 Excel 文件表头时调用的方法。可用于对表头进行校验或者记录日志等操作@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));// 如果想转成成 Map<Integer,String>// 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener// 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换}//在读取到一条数据时调用的方法。T 表示读取到的数据类型。可以在该方法中对读取到的数据进行处理或者记录日志等操作@Overridepublic void invoke(ReadDemoData readDemoData, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSON.toJSONString(readDemoData));cachedDataList.add(readDemoData);//达到BATCH_COUNT了,清空缓存,并可以去做一些处理(如存储一次数据库)//目的:防止几万条数据在内存中,容易OOMif(cachedDataList.size() >= BATCH_COUNT){//一些业务操作(如存储数据库)//……saveData();//清除缓存cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}//在读取 Excel 文件中除数据外的其他内容时调用的方法。例如,批注、超链接等。可以在该方法中进行相应的处理@Overridepublic void extra(CellExtra cellExtra, AnalysisContext analysisContext) {}//在读取数据完成后调用的方法。可以在该方法中进行一些资源清理工作或者记录日志等操作@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//一些业务操作(如存储数据库,简单打印剩余数据)cachedDataList.stream().forEach(System.out::println);log.info("所有数据解析完成!");}//判断是否还有下一条数据需要读取。如果返回 true,会自动调用 invoke(T data, AnalysisContext analysisContext) 方法来读取下一条数据;// 如果返回 false,则结束读取数据的过程。@Overridepublic boolean hasNext(AnalysisContext analysisContext) {return true;}//模拟数据存储(这里就是简单的打印一下)private void saveData(){log.info("{}条数据,开始存储数据库!", cachedDataList.size());cachedDataList.stream().forEach(System.out::println);log.info("存储数据库成功!");}
}

②单元测试

 /*** 7、读取表头数据** <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}* <p>* 3. 直接读即可*/
@Test
public void headerRead() {String fileName = getExcelUrl("readExcel.xlsx");// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, ReadDemoData.class, new DemoHeadDataListener()).sheet().doRead();
}

运行结果:

在这里插入图片描述

2、写Excel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/587443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

私有部署ELK,搭建自己的日志中心(三)-- Logstash的安装与使用

一、部署ELK 上文把采集端filebeat如何使用介绍完&#xff0c;现在随着数据的链路&#xff0c;继续~~ 同样&#xff0c;使用docker-compose部署&#xff1a; version: "3" services:elasticsearch:container_name: elasticsearchimage: elastic/elasticsearch:7.9…

git基础概念和常用命令(日常开发收藏备用)

目录 ### 常用命令 ### 远程仓库与克隆 ### 分支管理 ### 子模块&#xff08;Submodule&#xff09; ### 其他高级操作 ### 交互式暂存&#xff08;Interactive Staging&#xff09; ### cherry-pick ### rebase ### reflog与reset ### 子树合并&#xff08;Subtree …

【LearnOpenGL基础入门——5】着色器

目录 一.简介 二.GLSL 三.数据类型 四.输入与输出 五.Uniform 六.更多属性 一.简介 着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说&#xff0c;着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立…

【基础】【Python网络爬虫】【5.数据解析】bs4、Xpath、Parsel模块、正则表达式(附大量案例代码)(建议收藏)

Python网络爬虫基础 数据解析1. 为何数据解析2. 常见的数据类型结构化数据半结构化数据非结构化数据 3. 爬虫项目实现步骤 数据解析模块1. Bs4环境安装bs4解析流程案例 - bs4碧血剑文本爬取 2. Xpath环境安装xpath解析的编码流程xpath表达式如何理解&#xff1f;案例 - 简历模板…

第3课 使用FFmpeg获取并播放音频流

本课对应源文件下载链接&#xff1a; https://download.csdn.net/download/XiBuQiuChong/88680079 FFmpeg作为一套庞大的音视频处理开源工具&#xff0c;其源码有太多值得研究的地方。但对于大多数初学者而言&#xff0c;如何快速利用相关的API写出自己想要的东西才是迫切需要…

Android studio 多界面的跳转和返回

一、新建一个Empty Activity项目&#xff1a; 二、修改activity_main.xml布局文件&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/a…

【头歌实训】PySpark Streaming 入门

文章目录 第1关&#xff1a;SparkStreaming 基础 与 套接字流任务描述相关知识Spark Streaming 简介Python 与 Spark StreamingPython Spark Streaming APISpark Streaming 初体验&#xff08;套接字流&#xff09; 编程要求测试说明答案代码 第2关&#xff1a;文件流任务描述相…

Resolume Arena(VJ音视频软件):创意无限,视听艺术的新境界

Resolume Arena是一款领先的VJ音视频软件&#xff0c;为创意人士提供了丰富的视觉效果和音频处理功能。无论是在舞台演出、音乐会还是派对活动中&#xff0c;Resolume Arena能够将音乐、视频和图像无缝地结合&#xff0c;创造出引人入胜的视听体验。 Resolume Arena具备强大的…

Nginx快速入门:nginx实现正向代理|反向代理和正向代理的区别(八)

0. 引言 我们之前讲解的一直是nginx的反向代理配置&#xff0c;关于正向代理的实现一直没有涉及&#xff0c;但在实际生产中正向代理也有非常广泛的应用场景&#xff0c;因此&#xff0c;今天我们将针对正向代理来深入学习。 1. 相关概念 1.1 什么是反向代理 所谓反向代理&…

读算法霸权笔记08_反馈

1. 关开门 1.1. clopening 1.2. 指的是一个职员工作到很晚&#xff0c;关闭门店或者咖啡店&#xff0c;几小时后又在天亮之前回来开门 1.3. 极度不规律的工作时间安排越来越常见 1.3.1. 首当其冲的是星巴克、麦当劳和沃尔玛等企业的低薪职工 1.3.2. 不规律的工作时间安排是…

云安全指的是什么,云安全产品有哪些,有什么作用

云安全是什么意思呢&#xff1f;简单来说云安全是指基于云计算商业模式应用的安全软件&#xff0c;专门保护云计算系统的网络安全学科。那么云安全和传统安全有什么区别呢&#xff1f;云安全包括在基于在线的基础架构、应用程序和平台上保持数据的私密和安全比起传统的安全要求…

信号与线性系统翻转课堂笔记17——z变换及其性质

信号与线性系统翻转课堂笔记17——z变换及其性质 The Flipped Classroom17 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff09;序列的z变换…

Serverless架构:无服务器应用与AWS Lambda-读书笔记

Serverless架构&#xff1a;无服务器应用与AWS Lambda-读书笔记 好的架构可以成就软件&#xff0c;缺乏架构则会破坏软件。 一、Serverless 架构的来龙去脉 在典型的Web应用程序中&#xff0c;服务器接受前端的HTTP请求并处理请求。在保存到数据库之前&#xff0c;数据可能会…

使用 SSH 方式实现 Git 远程连接GitHub

git是目前世界上最先进的分布式版本控制系统&#xff0c;相比于SVN&#xff0c;分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在&#xff0c;也就是有没有联网都可以正常工作&#xff01;当有网络的时候&#xff0c;再把本地提交推送一下就完成了同步&…

C:Huffman编码a

【问题描述】 给定一组字符的Huffman编码表&#xff08;从标准输入读取&#xff09;&#xff0c;以及一个用该编码表进行编码的Huffman编码文件&#xff08;存在当前目录下的in.txt中&#xff09;&#xff0c;编写程序实现对Huffman编码文件的解码&#xff0c;并按照后序遍历序…

【Java开发岗面试】八股文—Java基础集合多线程

声明&#xff1a; 背景&#xff1a;本人为24届双非硕校招生&#xff0c;已经完整经历了一次秋招&#xff0c;拿到了三个offer。本专题旨在分享自己的一些Java开发岗面试经验&#xff08;主要是校招&#xff09;&#xff0c;包括我自己总结的八股文、算法、项目介绍、HR面和面试…

Python编程-面向对象基础与入门到实践一书的内容拓展

Python编程-面向对象基础与入门到实践一书的内容拓展 通过编程&#xff0c;模拟现实生活中的事物编程&#xff0c;叫做面向对象编程&#xff0c;此过程也叫做实例化编程 简单类的创建 class Test():def __init__ (self,id):self.id iddef print_id(self):print(self.id)这里建…

c++简易AI

今天小编一时雅兴大发&#xff0c;做了一个c的简易AI&#xff0c;还是很垃圾的&#xff01; 题外话&#xff08;每期都会有&#xff09;&#xff1a;我的蛋仔名叫酷影kuying&#xff0c;大家能加我好友吗&#xff1f; 上代码咯&#xff01; #include<bits/stdc.h> #in…

文件描述符

文件描述符 2.1 文件描述符 文件描述符&#xff08;File Descriptor&#xff09;是在Unix-like操作系统中用于标识和访问文件或I/O设备的抽象概念。它是一个非负整数&#xff0c;用于**唯一标识一个打开的文件、套接字&#xff08;socket&#xff09;或其他类型的I/O资源。**…

Elasticsearch-8.11.1 (2+1)HA(高可用)集群部署

目录 一、环境描述 二、安装 ES 2.1 下载Elasticsearch 2.2 解压Elasticsearch 2.3 创建es服务账号/密码 2.3 修改服务器配置 2.4 配置节点 2.4.1 配置说明 2.4.2 配置高可用集群 2.4.2.1 maser节点服务配置 2.4.2.2 node1 节点服务配置 2.4.2.3 node2 节点服务配置…