一、介绍
EasyExcel是阿里巴巴开源的,一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
其特点是:
- 快速的读取excel中的数据。
- 映射excel和实体类,让代码变的更加简洁。
- 在读写大文件的时候使用磁盘做缓存,更加的节约内存。
二、使用EasyExcel写
2.1 导入依赖
<!-- easyexcel依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
2.2 导出表格极简入门
①声明一个简单的对象
@Getter@Setter@EqualsAndHashCodepublicclassDemoData{@ExcelProperty("字符串标题")//这里默认为value属性赋值,value为导出的表格的表头privateString string;@ExcelProperty("日期标题")privateDate date;@ExcelProperty("数字标题")privateDouble doubleData;@ExcelIgnore//忽略这个字段,生成的表格中就不会有该字段privateString ignore;}
②写一个简单的表格导出案例
publicclassSimpleWriteTest{@TestpublicvoidsimpleWrite(){//1.模拟要写出数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.设置写出的文件路径String fileName ="demo1.xlsx";//3.利用EasyExcel写出,会自动关流//write方法的参数1:文件路径,参数2:模板对象类型EasyExcel.write(fileName,DemoData.class).sheet("模板")//sheet页名字.doWrite(list);//要写出的数据}}
这样,就会在当前项目目录下生成名为
demo1.xlsx
的表格,内容如下:
2.3 根据参数导出指定属性
案例1:指定哪些属性不需要导出到表格中,仅导出剩余属性。
publicclassSimpleWriteTest{@TestpublicvoidexcludeWrite(){//1.模拟要写出数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.设置导出过程中剔除的字段Set<String> excludeColumnFiledNames =newHashSet<String>();
excludeColumnFiledNames.add("date");//这里剔除date属性//3.设置写出的文件路径String fileName ="demo2.xlsx";//4.写出EasyExcel.write(fileName,DemoData.class).excludeColumnFieldNames(excludeColumnFiledNames)//将要剔除的列设置给EasyExcel.sheet("模板").doWrite(list);}}
结果如下,就没有了日期列。
案例2:指定仅导出哪些属性,剩余属性不导出。
publicclassSimpleWriteTest{@TestpublicvoidIncludeWrite(){//1.模拟要写出数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.设置仅导出哪些属性Set<String> includeColumnFiledNames =newHashSet<String>();
includeColumnFiledNames.add("date");//3.设置写出的文件路径String fileName ="demo3.xlsx";//4.写出EasyExcel.write(fileName,DemoData.class).includeColumnFieldNames(includeColumnFiledNames)//设置给EasyExcel.sheet("模板").doWrite(list);//这里导出的表格中就只有:时间字段。}}
2.4 指定写入的列
使用
@ExcelProperty
注解的
index
属性指定要导入列的下标。
@Getter@Setter@EqualsAndHashCodepublicclassIndexData{@ExcelProperty(value ="字符串标题", index =0)privateString string;@ExcelProperty(value ="日期标题", index =1)privateDate date;//这里设置3 会导致第三列空的@ExcelProperty(value ="数字标题", index =3)privateDouble doubleData;}
publicclassWriteTest{@TestpublicvoidwriteDemo(){//1.模拟要写出数据List<IndexData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){IndexData data =newIndexData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.设置写出的文件路径String fileName ="demo4.xlsx";//3.写出EasyExcel.write(fileName,IndexData.class).sheet("模板").doWrite(list);}}
能看到第三列是空的:
2.5 复杂头写入
在导出表格的时候,会因为业务需求,导出复杂的表头,咱们可以使用
@ExcelProperty
注解的
value
属性来实现这个需求。
例如我们要导出一个如图的表格:
仅需通过
value
属性设置即可:
@Getter@Setter@EqualsAndHashCodepublicclassComplexHeadData{@ExcelProperty(value={"主标题","副标题1","字符串标题"})privateString string;@ExcelProperty(value={"主标题","副标题1","日期标题"})privateDate date;@ExcelProperty(value={"主标题","副标题2","数字标题"})privateDouble doubleData;}
导出的代码:
publicclassWriteTest{@TestpublicvoidcomplexHeadWrite(){List<ComplexHeadData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){ComplexHeadData data =newComplexHeadData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.设置写出的文件路径String fileName ="demo5.xlsx";//3.导出EasyExcel.write(fileName,ComplexHeadData.class).sheet("模板").doWrite(list);}}
2.6 写出到多个sheet
2.6.1 多个sheet中写入相同类型对象
publicclassWriteTest{@TestpublicvoidrepeatedWrite(){//1.模拟要写出数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}String fileName ="demo6.xlsx";//2.创建 ExcelWriter对象,并指定文件名和对象类型try(ExcelWriter excelWriter =EasyExcel.write(fileName,DemoData.class).build()){// 这里模拟5次for(int i =0; i <5; i++){// 每次都要创建writeSheet,这里注意必须指定sheetNo(sheet页编号),而且sheetName(sheet也名字)必须不一样WriteSheet writeSheet =EasyExcel.writerSheet(i,"模板"+ i).build();// 将数据写入到sheet页中,最终,会生成5个sheet页
excelWriter.write(list, writeSheet);}}}}
效果见下图:
2.6.2 多个sheet中写入不同类型对象
publicclassWriteTest{@TestpublicvoidrepeatedWrite1(){//1.模拟要写出数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}String fileName ="demo7.xlsx";//2.创建ExcelWriter对象,仅需指定文件名,因为写入的数据是不固定的,所以不能写死try(ExcelWriter excelWriter =EasyExcel.write(fileName).build()){// 模拟5次for(int i =0; i <5; i++){// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。// 注意:head方法中的DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class,实际上可以一直变WriteSheet writeSheet =EasyExcel.writerSheet(i,"模板"+ i).head(DemoData.class).build();// 写出数据
excelWriter.write(list, writeSheet);}}}}
2.7 日期、数字或者自定义格式转换
在写出数据时,如果需要对数据进行格式设置,可以为对象进行如下设置:
@Getter@Setter@EqualsAndHashCodepublicclassConverterData{/**
* 我想所有的 字符串起前面加上"自定义:"三个字
*/@ExcelProperty(value ="字符串标题", converter =CustomStringStringConverter.class)privateString string;/**
* 我想写到excel 用年月日的格式
*/@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")@ExcelProperty("日期标题")privateDate date;/**
* 我想写到excel 用百分比表示
*/@NumberFormat("#.##%")@ExcelProperty(value ="数字标题")privateDouble doubleData;}
自定义格式:
publicclassCustomStringStringConverterimplementsConverter<String>{@OverridepublicClass<?>supportJavaTypeKey(){returnString.class;}@OverridepublicCellDataTypeEnumsupportExcelTypeKey(){returnCellDataTypeEnum.STRING;}/**
* 这里读的时候会调用,先不管
*/@OverridepublicStringconvertToJavaData(ReadConverterContext<?> context){return"自定义:"+ context.getReadCellData().getStringValue();}/**
* 这里是写的时候会调用
* 会在原有内容之前拼接上:"自定义:"
*/@OverridepublicWriteCellData<?>convertToExcelData(WriteConverterContext<String> context){returnnewWriteCellData<>("自定义:"+context.getValue());}}
写出的代码:
publicclassSimpleWriteTest{@TestpublicvoidconverterWrite(){//1.模拟要写出数据List<ConverterData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){ConverterData data =newConverterData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}String fileName ="demo8.xlsx";//3.写出EasyExcel.write(fileName,ConverterData.class).sheet("模板").doWrite(list);}}
效果如下:
2.8 设置列宽,行高
使用注解在对象的属性上进行设置即可
@Getter@Setter@EqualsAndHashCode@ContentRowHeight(20)//内容高度为:20@HeadRowHeight(40)//表头高度为:40@ColumnWidth(25)//列宽为:25publicclassWidthAndHeightData{@ExcelProperty("字符串标题")privateString string;@ExcelProperty("日期标题")privateDate date;//单独设置宽度为50@ColumnWidth(50)@ExcelProperty("数字标题")privateDouble doubleData;}
导出代码为:
publicclassWriteTest{@TestpublicvoidwidthAndHeightWrite(){//1.模拟要写出数据List<WidthAndHeightData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){WidthAndHeightData data =newWidthAndHeightData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}String fileName ="demo9.xlsx";//3.写出EasyExcel.write(fileName,WidthAndHeightData.class).sheet("模板").doWrite(list);}}
2.9 合并单元格
使用注解来设置合并规则:
@Getter@Setter@EqualsAndHashCode// 将第6-7行的2-3列合并成一个单元格// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)publicclassDemoMergeData{// 这一列 每隔2行 合并单元格@ContentLoopMerge(eachRow =2)@ExcelProperty("字符串标题")privateString string;@ExcelProperty("日期标题")privateDate date;@ExcelProperty("数字标题")privateDouble doubleData;}
写出:
publicclassSimpleWriteTest{@TestpublicvoidmergeWrite(){//1.模拟要写出数据List<DemoMergeData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoMergeData data =newDemoMergeData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}String fileName ="demo10.xlsx";//2.写出EasyExcel.write(fileName,DemoMergeData.class).sheet("模板").doWrite(list);}}
效果如下
2.10 在web项目中实现excel的导出
2.10.1 客户端页面
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><ahref="/download">导出excel</a></body></html>
2.10.2 服务端代码
@ControllerpublicclassDownloadController{@GetMapping("/download")publicvoiddownload(HttpServletResponse response)throwsIOException{//1.模拟数据List<DemoData> list =ListUtils.newArrayList();for(int i =0; i <10; i++){DemoData data =newDemoData();
data.setString("字符串"+ i);
data.setDate(newDate());
data.setDoubleData(0.56);
list.add(data);}//2.执行导出//设置响应类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");//设置编码
response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止文件名中文乱码,当然和easyexcel没有关系String fileName =URLEncoder.encode("测试","UTF-8").replaceAll("\\+","%20");//设置文件名
response.setHeader("Content-disposition","attachment;filename*=utf-8''"+ fileName +".xlsx");//导出EasyExcel.write(response.getOutputStream(),DemoData.class).sheet("模板").doWrite(list);}}
三、使用EasyExcel读
easyexcel读取表格内容是基于监听器方式实现的,我们需要实现它提供的
ReadListener
接口,进而实现读取的功能。
3.1 极简入门
假如有表格
demo.xlsx
的内容如下:
① 首先我们需要准备一个对象与之对应:
@Getter@Setter@EqualsAndHashCodepublicclassDemoData{@ExcelProperty("字符串标题")privateString string;@ExcelProperty("日期标题")privateDate date;@ExcelProperty("数字标题")privateDouble doubleData;}
② 准备监听器
publicclassDemoDataListenerimplementsReadListener<DemoData>{/**
* 每存储100条,可以存储一次数据库,然后清理list ,方便内存回收
*/privatestaticfinalint BATCH_COUNT =100;/**
* 用于缓存的数据的集合
*/privateList<DemoData> cachedDataList =ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/* 在实际开发中我们可以使用有参构造,将自己的 dao 或者 service传进来,进而实现数据入库
private DemoDAO demoDAO;
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
*//**
* 这个每一条数据解析都会来调用
*/@Overridepublicvoidinvoke(DemoData data,AnalysisContext context){System.out.println("每次读到内容是:"+data);
cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif(cachedDataList.size()>= BATCH_COUNT){//1.存储数据库//demoDAO.saveBatch(cachedDataList);//2.存储完成清理 list
cachedDataList =ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/**
* 所有数据解析完成了 都会来调用
*/@OverridepublicvoiddoAfterAllAnalysed(AnalysisContext context){System.out.println("所有数据解析完成!");}}
③ 读取表格内容
publicclassReadTest{@TestpublicvoidsimpleRead(){String fileName ="demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName,DemoData.class,newDemoDataListener()).sheet().doRead();}}
3.2 在web中的读
@PostMapping("/upload")@ResponseBodypublicStringupload(MultipartFile file)throwsIOException{//read方法参数说明://参数1:文件输入流//参数2:与表格数据对应的对象类型//参数3:监听器EasyExcel.read(file.getInputStream(),UploadData.class,newUploadDataListener(uploadDAO)).sheet().doRead();return"success";}
版权归原作者 码赛客1024 所有, 如有侵权,请联系我们删除。