7 行代码优雅地实现 Excel 文件生成&下载功能
2024-06-19 0
一、简介
关于Excel文件的导出,可以说是大多数服务中都需要集成的功能。那么,如何优雅且快速(懒惰地)实现这个功能呢?
你的第一个想法可能是:这不是很简单吗?这可以使用开源框架Apachepoi或jxl来实现。给百度写代码,复制代码模板,然后修改成适合自己的业务,能有多难?
嗯这并不难,但是您的代码可能如下所示:
上面的代码是不是显得又臭又长?今天小哈就教你如何用7行代码生成Excel文件!
2Apachepoi和jxl缺陷
在讲如何实现之前,我们先来讨论一下传统Excel框架的缺点!除了上述之外,Apachepoi和jxl都不够简单、优雅和快速生成Excel文件。它们还有一个严重的问题,那就是它们消耗大量内存,严重时会导致内存溢出。
虽然POI是目前使用最广泛的Excel分析框架,但这个框架并不完美。
为什么这么说?
开发者大多使用userModel模式来使用POI。userModel的优点是易于上手和使用。您只需复制代码并运行即可。剩下的就是写业务转换了。虽然转换需要数百行代码,但仍然是可控的。
然而userModel模式最大的问题是消耗大量内存。解析一个几兆字节的文件可能会使用数百兆字节的内存。现实情况是,现在许多应用程序都使用这种模式。他们之所以还能正常运转,是因为竞争并不激烈。当并发量增大时,会频繁出现OOM或者fullgc。
3阿里巴巴出品的EasyExcel,安利一波
什么是EasyExcel?顾名思义,它使使用Excel变得非常愉快。我们看一下EasyExcelGitHub官方的截图:
目前已有5519Star,其官方介绍是:
快速简单的Java处理Excel工具,避免OOM!
以下为官方介绍:
4EasyExcel解决什么问题?
主要有以下几点:
传统Excel框架,如Apachepoi、jxl等,存在内存溢出问题;
传统开源Excel框架复杂、使用繁琐;
EasyExcel的下层仍然使用POI,但是做了很多优化,比如修复了并发情况下的一些bug。具体修复细节可以阅读官方文档https://githubcom/alibaba/easyexcel;
5快速上手
51添加依赖
comalibabaeasyexcel112-beta5
52七行代码生成Excel
@TestpublicvoidwriteExcel1()throwsException{//文件输出位置OutputStreamoutnewFileOutputStream('/Users/a123123/Work/tmpfiles/testxlsx');ExcelWriterwriterEasyExcelFactorygetWriter(out);//写一个只有一张sheet的Excel文件,这种场景是比较复杂一般Sheetsheet1newSheet(1,0,WriteModelclass);//第一个sheet的名称sheet1setSheetName('firstsheet');//在Writer上下文中写入数据//输入参数1:创建要写入的文件模型数据//输入参数2:写入目的地sheetwriterwrite(createModelList(),sheet1);//将上下文中最终的outputStream写入指定文件writerfinish();//关闭流out();}
在上面的示例代码中,小哈强调了两个要点:
①:WriteModel对象是写入Excel的数据模型对象,**等一下,这似乎不适合你,对吧?未指定表标题和每个单元格中的数据顺序。是否能够达到想要的效果呢?别担心,我们稍后再谈!
②:创建需要写入的数据集当然,在正常活动中,这部分是从数据库中查询的。
PS:如果写入的数据量较大,需要先分片并查询后再写入,否则可能会出现OOM(OutofMemory)。
回过头来,我们看一下WriteModel对象内部发生了什么?
/***@作者微信公众号:小哈学Java*@网址:wwwexceptionsite*@date2019/5/9*@时间14:07*@discription编写Excel模板对象**/@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassWriteModelextendsBaseRowModel{@ExcelProperty(value'name',index0)privateStringname;@ExcelProperty(value'password',index1)privateStringpassword;@ExcelProperty(value'age',index2)privateIntegerage;}
ExayExcel提供了注释方法可以轻松定义Excel所需的数据模型:
①:首先定义的写入模型必须继承自BaseRowModeljava;
②:通过@ExcelProperty注解指定各字段的列名和下标位置;
同时,上面定义的createModelList()方法也非常简单。通过循环,创建一个写在模板上的List集合:
废话不多说,这个快速接入案例就介绍完了。我们来运行一下单元测试看看实际效果:
不管怎样,效果还是很棒的!
6特殊场景支持
在商业世界中我们也会有一些特殊的需求,比如下面这些。
61动态生成Excel内容
前面的示例是基于注释的,这意味着表头和内容是硬编码的。也就是说,我定义了一个数据模型,所以生成的Excel文件只能遵循这个模型。然而,实际业务需求可能会发生动态变化。我们应该怎样做呢?
@TestpublicvoidwriteExcel2()throwsException{//文件输出位置OutputStreamoutnewFileOutputStream('/Users/a123123/Work/tmpfiles/test2xlsx');ExcelWriterwriterEasyExcelFactorygetWriter(out);//动态添加表头,适合某些表头动态变化的场景Sheetsheet1newSheet(1,0);sheet1setSheetName('firstsheet');//使用Tableable1newTable(1)创建Sheet中使用的表格;//无注释模式,动态添加一个table1表头setHead(DataUtilcreateTestListStringHead)());//写入数据writerwrite1(createDynamicModelList(),sheet1,table1);//将上下文中最终的outputStream写入指定文件writerfinish();//关闭流outclose();}
①:无注释模式,动态添加标题,可以自由组合复杂标题。代码如下:
publicstaticList>createTestListStringHead(){//模板上没有注解,头数据动态传入List>headnewArrayList>();ListheadCoulumn1newArrayList();ListheadCoulumn2newArrayList();ListheadCoulumn3newArrayList();ListheadCoulumn4newArrayList();ListheadCoulumn5newArrayList();headCoulumn1add('第一列');headCoulumn1add('第一列');headCoulumn1add('第一列');headCoulumn2add('第一列');headCoulumn2add('第一列');headCoulumn2add('第一列');headCoulumn3add('第二列');headCoulumn3add('第二列');headCoulumn3add('第二列');headCoulumn4add('第三列');headCoulumn4add('第三列2');headCoulumn4add('第三列2');headCoulumn5add('第一列');headCoulumn5add('第三列');headCoulumn5add('第四列');headadd(headCoulumn1);headadd(headCoulumn2);headadd(headCoulumn3);headadd(headCoulumn4);headadd(headCoulumn5);returnhead;}
②:创建动态数据。注意这里的数据类型是Object:
运行单元测试,看看效果:
62自定义标题和内容样式
我想自定义表格标题和内容样式,我该怎么办?
我们重用了之前的示例代码并添加了额外的代码来设置自定义表格样式。createTableStyle()具体内容如下:
publicstaticTableStylecreateTableStyle(){TableStyletableStylenewTableStyle();//设置表格表头样式FontheadFontnewFont();//如果字体为粗体headFontsetBold(true);//字体大小headFontsetFontHeightInPoints((short)12);//字体头字体。setFontName('Kaiti');tableStylesetTableHeadFont(headFont);//背景颜色tableStylesetTableHeadBackGroundColor(IndexedColorsBLUE);//设置表格主体样式FontcontentFontnewFont();contentFontsetBold(true);contentFontsetFontHeightInPoints((短)12);contentFontsetFontName('黑体');tableStylesetTableContentFont(contentFont);tableStylesetTableContentBackGroundColor(IndexedColorsGREEN);returntableStyle;}
我们可以通过TableStyle类设置表头和主题的样式。
63合并单元格
我们可以使用merge()方法合并单元格:
请注意,索引从0开始,这意味着第六行到第七行被合并,第一列到第五列被合并。运行代码,观察效果:
64定制加工
对于更复杂的处理EasyExcel保留了WriterHandler接口,允许您自定义处理代码:
接口中定义了三个方法:
sheet():自定义每个sheet创建后业务逻辑的处理;
row():自定义创建每一行后业务逻辑的处理;
cell():自定义创建每个cell后业务逻辑的处理;
实现接口后,编写自定义逻辑处理代码,然后调用静态getWriterWithTempAndHandler()方法获取ExcelWriter对象,只需传递实现类即可。
例如,以下示例代码:
ExcelWriterwriterEasyExcelFactorygetWriterWithTempAndHandler(null,out,ExcelTypeEnumXLSX,true,newMyWriterHandler());
7网上下载示例代码
publicclassDown{@GetMapping('/ahtm')publicvoidcooperation(HttpServletRequestrequest,HttpServletResponseresponse){StringfileNamenewString(('UserInfo'newYes)mpleDateFormat('yyyy-MM-dd')format(newDate()))getBytes(),'UTF-8');ServletOutputStreamoutresponsegetOutputStream();responsesetContentType('multipart/form-data');响应setCharacterEncoding('utf-8');responsesetHeader('内容布局','附件;文件名'文件名'xlsx');ExcelWriterwriternewExcelWriter(out,ExcelTypeEnumXLSX,true);Sheetsheet1newSheet(1,0);sheet1setSheetName('第一张');writerwrite0(getListString(),sheet1);writerfinish();outflush();}}}
八、注意事项
81大数据写入时需要分片
例如,当我们需要从数据库中查询大量数据时,我们需要进行企业级分片处理,即需要多次查询然后写入,以避免OOM内存溢出。
82Excel最大行数问题
Excel03和07版本都有行数和列数的限制:
由于csv是文本文件,因此最大行数实际上没有限制。但是用Excel客户端打开时,仍然行数太多,不显示。
换句话说,如果你想写更多的行,那是不可能的。如果强行这样做,程序会抛出类似下面的异常
无效行号(1048576)超出允许范围(01048575)
怎么解决呢?
写入多个Excel文件;
同一个Excel文件写入多张表;
9总结
今天,小哈向她的朋友们介绍了EasyExcel,解释了为什么要使用它,并演示了相关的示例代码。当然,除了写入Excel文件之外,EasyExcel还具有快速读取Excel的功能,由于本文主要介绍:如何优雅地生成Excel文件,感兴趣的朋友还可以访问GitHub官方网站查看相关文档。
源码获取地址:kdocscn/l/ckKzeMtFitEy
本站文章均由用户上传或转载而来,该文章内容本站无法检测是否存在侵权,如果本文存在侵权,请联系邮箱:2287318951@qq.com告知,本站在7天内对其进行处理。