EasyExcel
EasyExcel是阿里推出的一款高性能的开源excel读写框架,主要解决读大文件poi占内存大的问题,同时也提供了一些简单的excel操作API。2.0.5以后的改动比较大,结合网上的,自己整理了一个demo,方便以后复用。
文档:https://www.yuque.com/easyexcel/doc/easyexcel
本文git:https://gitee.com/proper128/springboot-easyexcel
目录
1. 引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>
</dependency>
2. 工具类 EasyExcelUtil
public class EasyExcelUtil {
/**
* web导出excel文件
* @param response response
* @param list 实体数据
* @param fileName 文件名
* @param sheetName sheet名
* @param clazz 实体类
* @throws Exception
*/
public static void exprotExcel(HttpServletResponse response, List<?> list, String fileName, String sheetName,Class<?> clazz)throws Exception {
EasyExcel.write(getOutputStream(fileName,response), clazz)
.sheet(sheetName)
.doWrite(list);
}
/**
* 导出文件时为Writer生成OutputStream
* @param fileName
* @param response
* @return
*/
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
} catch (IOException e) {
throw new Exception("导出excel表格失败!", e);
}
}
/**
* 同步无模型读(默认读取sheet0,从第2行开始读)
*
* @param filePath
* @return
*/
public static List<Map<Integer, String>> syncRead(String filePath) {
return EasyExcelFactory.read(filePath).sheet().doReadSync();
}
/**
* 同步无模型读(默认表头占一行,从第2行开始读)
*
* @param filePath
* @param sheetNo sheet页号,从0开始
* @return
*/
public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return List<Map < colNum, cellValue>>
*/
public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return List<Map < colNum, cellValue>>
*/
public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return List<Map < colNum, cellValue>>
*/
public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步按模型读(默认读取sheet0,从第2行开始读)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @return
*/
public static List<T> syncReadModel(String filePath, Class clazz) {
return EasyExcelFactory.read(filePath).sheet().head(clazz).doReadSync();
}
/**
* 同步按模型读(默认表头占一行,从第2行开始读)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @return
*/
public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param file
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static List<T> syncReadModel(File file, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 异步无模型读(默认读取sheet0,从第2行开始读)
*
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener) {
EasyExcelFactory.read(filePath, excelListener).sheet().doRead();
}
/**
* 异步无模型读(默认表头占一行,从第2行开始读)
*
* @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @return
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo) {
EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static void asyncRead(InputStream inputStream, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(inputStream, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static void asyncRead(File file, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取(默认读取sheet0,从第2行开始读)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet().doRead();
}
/**
* 异步按模型读取(默认表头占一行,从第2行开始读)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).doRead();
}
/**
* 异步按模型读取
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(inputStream, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(File file, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 无模板写文件
*
* @param filePath
* @param head 表头数据
* @param data 表内容数据
*/
public static void write(String filePath, List<List<String>> head, List<List<Object>> data) {
EasyExcel.write(filePath).head(head).sheet().doWrite(data);
}
/**
* 无模板写文件
*
* @param filePath
* @param head 表头数据
* @param data 表内容数据
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, List<List<String>> head, List<List<Object>> data, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 根据excel模板文件写入文件
*
* @param filePath
* @param templateFileName
* @param headClazz
* @param data
*/
public static void writeTemplate(String filePath, String templateFileName, Class headClazz, List data) {
EasyExcel.write(filePath, headClazz).withTemplate(templateFileName).sheet().doWrite(data);
}
/**
* 根据excel模板文件写入文件
*
* @param filePath
* @param templateFileName
* @param data
*/
public static void writeTemplate(String filePath, String templateFileName, List data) {
EasyExcel.write(filePath).withTemplate(templateFileName).sheet().doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
*/
public static void write(String filePath, Class headClazz, List data) {
EasyExcel.write(filePath, headClazz).sheet().doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, Class headClazz, List data, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用)
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, Class headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件(包含某些字段)
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param includeCols 过滤包含的字段,根据字段名称过滤
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void writeInclude(String filePath, Class headClazz, List data, Set<String> includeCols, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件(排除某些字段)
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param excludeCols 过滤排除的字段,根据字段名称过滤
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void writeExclude(String filePath, Class headClazz, List data, Set<String> excludeCols, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(outputStream)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param outputStream
* @return
*/
public static EasyExcelWriterFactory writeWithSheets(OutputStream outputStream) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(outputStream);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(file)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param file
* @return
*/
public static EasyExcelWriterFactory writeWithSheets(File file) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(file);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(filePath)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param filePath
* @return
*/
public static EasyExcelWriterFactory writeWithSheets(String filePath) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(filePath);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel)
* ExcelUtil.writeWithSheets(response, exportFileName)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param response
* @param exportFileName 导出的文件名称
* @return
*/
public static EasyExcelWriterFactory writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode(exportFileName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(response.getOutputStream());
return excelWriter;
}
}
3. 监听器 UserExcelListener
/**
* @description: 模型解析监听器--> 2.0.5以后每个模型创建一个监听器
* 每解析一行会回调invoke()方法,整个excel解析结束会执行doAfterAllAnalysed()方法
* 有个很重要的点 UserExcelListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
* @author: Li Kang
* @create: 2020-05-04 11:08
*/
@Slf4j
public class UserExcelListener<User> extends AnalysisEventListener<User> {
private final List<User> userList = Lists.newArrayList();
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
// -----------------------------------------------------------------------------------
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private UserDAO userDAO;
public UserExcelListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
userDAO = new UserDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param userDAO
*/
public UserExcelListener(UserDAO userDAO) {
this.userDAO = userDAO;
}
// -----------------------------------------------------------------------------------
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
@Override
public void invoke(User user, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(user));
userList.add(user);
// 实际数据量比较大时,rows里的数据可以存到一定量之后进行批量处理(比如存到数据库),
// 然后清空列表,以防止内存占用过多造成OOM
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (userList.size() >= BATCH_COUNT) {
saveUserList();
// 存储完成清理 list
userList.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("read {} rows", userList.size());
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveUserList();
}
/**
* 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
}
}
public List<User> getRows() {
return userList;
}
/**
* 加上存储数据库
*/
private void saveUserList() {
log.info("{}条数据,开始存储数据库!", userList.size());
userDAO.save((List<com.lk.model.User>) userList);
log.info("存储数据库成功!");
}
}
4. EasyExcelWriterFactory
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import java.io.File;
import java.io.OutputStream;
import java.util.List;
/**
* 自定义EasyExcel写工厂
*/
public class EasyExcelWriterFactory
{
private int sheetNo = 0;
private ExcelWriter excelWriter = null;
public EasyExcelWriterFactory(OutputStream outputStream) {
excelWriter = EasyExcel.write(outputStream).build();
}
public EasyExcelWriterFactory(File file) {
excelWriter = EasyExcel.write(file).build();
}
public EasyExcelWriterFactory(String filePath) {
excelWriter = EasyExcel.write(filePath).build();
}
/**
* 链式模板表头写入
* @param headClazz 表头格式
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
* @return
*/
public EasyExcelWriterFactory writeModel(Class headClazz, List data, String sheetName){
excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(headClazz).build());
return this;
}
/**
* 链式自定义表头写入
* @param head
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
* @param sheetName
* @return
*/
public EasyExcelWriterFactory write(List<List<String>> head, List data, String sheetName){
excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(head).build());
return this;
}
public void finish() {
excelWriter.finish();
}
}
5. 实体类 User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@ExcelProperty(value = "用户名", index = 0)
private String username ;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String password ;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
@ExcelProperty(value = "注册时间", index = 2)
@DateTimeFormat("yyyy/MM/dd")
private Date createTime;
}
6. DAO
/**
* @description: 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
* @author: Li Kang
* @create: 2020-05-04 17:16
*/
@Slf4j
public class UserDAO {
public void save(List<User> userList) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
log.info("insert操作");
}
}
7. Controller
@Controller
public class TestController {
/**
* 导出测试
* @param response
* @throws Exception
*/
@GetMapping("/exportTest")
void exportTest(HttpServletResponse response) throws Exception {
//单sheet,单table导出测试
List<User> userList = Lists.newArrayList();
for (int i = 0; i < 5 ; i++) {
User user = new User("admin"+i,"123"+i,1+i,new Date());
userList.add(user);
}
EasyExcelUtil.exprotExcel(response,userList,"导出测试","sheet单1",User.class);
}
/**
* 上传测试
* @param file
* @return
* @throws IOException
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
// EasyExcel.read(file.getInputStream(), User.class, new UploadDataListener(uploadDAO)).sheet().doRead();
EasyExcelUtil.asyncReadModel(file.getInputStream(),new UserExcelListener<>(),User.class,0,0);
return "success";
}
}
8. 测试类
@Slf4j
@SpringBootTest
public class ApplicationTests {
@BeforeEach
void testBefore() {
log.info("测试开始!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
@AfterEach
void testAfter() {
log.info("测试结束!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
@Test
public void write() {
List<User> userList = Lists.newArrayList();
for (int i = 0; i < 11; i++) {
User user = new User("admin" + i, "123" + i, 18 + i,new Date());
userList.add(user);
}
EasyExcelUtil.write("E:\3.xlsx", User.class, userList);
}
@Test
public void read() {
String fileName = "E:\3.xlsx";
// 同步读
// List<T> ts = EasyExcelUtil.syncReadModel(fileName, User.class);
// log.info(JSON.toJSONString(ts));
// 异步读
EasyExcelUtil.asyncReadModel(fileName,new UserExcelListener<>(),User.class);
}
}