惊了!7 行代码优雅地实现 Excel 文件导出功能?

论坛 期权论坛 期权     
JAVA葵花宝典   2019-7-14 04:20   2402   0

[h1]作者:犬小哈[/h1][h1]目录[/h1]
  • 一、前言
  • 二、Apache poi、jxl 的缺陷
  • 三、阿里出品的 EasyExcel,安利一波
  • 四、EasyExcel 解决了什么
  • 五、快速上手
  • 六、特殊场景支持
  • 七、Web 下载示例代码
  • 八、需要注意的点
  • 九、总结
[h1]一、前言[/h1]关于导出 Excel 文件,可以说是大多数服务中都需要集成的功能。那么,要如何优雅快速地(偷懒地)去实现这个功能呢?
你可能第一想法是:这还不简单?用 Apache 开源框架 poi, 或者 jxl 都可以实现啊。面向百度编程,把代码模板 copy 下来,根据自己的业务再改改,能有多难?


嗯.. 的确不难,但是你的代码可能是下面这个熊样子的:


上面这段代码看上去是不是又臭又长呢?今天,小哈将教您如何使用 7 行代码搞定 Excel 文件生成功能!


[h1]二、Apache poi、jxl 的缺陷[/h1]在说如何实现之前,我们先来讨论一下传统 Excel 框架的不足!除了上面说的,Apache poi、jxl 都存在生成 excel 文件不够简单优雅快速外,它们都还存在一个严重的问题,那就是非常耗内存,严重时会导致内存溢出。
POI 虽然目前来说,是 excel 解析框架中被使用最广泛的,但这个框架并不完美。
为什么这么说呢?
开发者们大部分使用 POI,都是使用其 userModel 模式。而 userModel 的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,但是还是可控的。
然而 userModel 模式最大的问题是在于,对内存消耗非常大,一个几兆的文件解析甚至要用掉上百兆的内存。现实情况是,很多应用现在都在采用这种模式,之所以还正常在跑是因为并发不大,并发上来后,一定会OOM或者频繁的 full gc。
[h1]三、阿里出品的 EasyExcel,安利一波[/h1]什么是 EasyExcel? 见名知意,就是让你操作 Excel 异常的酸爽。先来看下 EasyExcel GitHub 官方截图:


截止目前为止已有 5519 Star, 官方对其的简介是:
快速、简单避免OOM的java处理Excel工具!
以下是官方介绍:


[h1]四、EasyExcel 解决了什么[/h1]主要来说,有以下几点:
  • 传统 Excel 框架,如 Apache poi、jxl 都存在内存溢出的问题;
  • 传统 excel 开源框架使用复杂、繁琐;
  • EasyExcel 底层还是使用了 poi, 但是做了很多优化,如修复了并发情况下的一些 bug, 具体修复细节,可阅读官方文档https://github.com/alibaba/easyexcel;


[h1]五、快速上手[/h1][h2]5.1 添加依赖[/h2]
    1. [/code]
    2. [*][code]
    复制代码
    1.   com.alibaba
    复制代码
    1.   easyexcel
    复制代码
    1.   1.1.2-beta5
    复制代码
    1. [/code]
    2. [/list][h2]5.2 七行代码搞定 Excel 生成[/h2][img]https://201907.oss-cn-shanghai.aliyuncs.com/wc/1811847-e5e7a7f829647cafd3f37eac3d07510f[/img]
    3. 上面这段示例代码中,有两个点很重要,小哈已经重点标注标:
    4. [list][*]①:WriteModel 这个对象就是要写入 Excel 的数据模型对象,等等,你这好像不行吧?表头 head,以及每个单元格内的数据顺序都没指定,能达到想要的效果么?别急,后面会讨论这块!
    5. [*]②:创建需要写入的数据集,当然了,正常业务中,这块都是从数据库中查询出来的。
    6. [/list][quote]PS: 如果说写入的数据量很大,需要做分片查询再写入的处理,否则可能会 OOM(Out of Memory).
    7. [/quote]回过头来,我们来看看 [code]WriteModel
    复制代码
    这个对象内部到底有什么幺蛾子!


    ExayExcel 提供注解的方式, 来方便的定义 Excel 需要的数据模型:
    • ①:首先,定义的写入模型必须要继承自
      1. BaseRowModel.java
      复制代码
      ;
    • ②:通过
      1. @ExcelProperty
      复制代码
      注解来指定每个字段的列名称,以及下标位置;
    同时,上面定义的
    1. createModelList()
    复制代码
    方法也很简单,通过循环,创建一个写入模型的 List 集合:


    废话不多说,这个快速接入的案例也介绍的差不多了,跑一跑单元测试看下实际效果:


    怎么样,效果还是挺棒棒的!


    [h1]六、特殊场景支持[/h1]在实际的业务中,我们还会有一些特需的需求,比如说下面这些。
    [h2]6.1 动态生成 Excel 内容[/h2]上面的例子是基于注解的,也就是说表头 head, 以及内容都是写死的,换句话说,我定义好了一个数据模型,那么,生成的 Excel 文件也就是只能遵循这种模型来了,但是,实际业务中可能会存在动态变化的需求,要怎么做呢?


    • ①:无注解模式,动态添加表头,也可自由组合复杂表头,代码如下:


    • ②:创建动态数据,注意这里的数据类型是
      1. Object
      复制代码
      :


    跑一下单元测试,看下效果:


    [h2]6.2 自定义表头以及内容样式[/h2]我想自定义表头,内容样式,咋办?

    我们复用了上面的示例代码,并额外添加了设置自定义表格样式的代码,
    1. createTableStytle()
    复制代码
    具体内容如下:


    我们可以通过
    1. TableStyle
    复制代码
    这个类来设置表头、表格主题的样式。
    [h2]6.3 合并单元格[/h2]我们可以通过
    1. merge()
    复制代码
    方法来合并单元格:


    注意下标是从 0 开始的,也就是说合并了第六行到第七行,其中的第一列到第五列,跑下代码,看下效果:


    [h2]6.4 自定义处理[/h2]对于更复杂的处理,EasyExcel 预留了
    1. WriterHandler
    复制代码
    接口来,允许你自定义处理代码:


    接口中定义了三个方法:
      1. sheet()
      复制代码
      : 在创建每个 sheet 后自定义业务逻辑处理;
      1. row()
      复制代码
      : 在创建每个 row 后自定义业务逻辑处理;
      1. cell()
      复制代码
      : 在创建每个 cell 后自定义业务逻辑处理;
    我们实现了该接口后,编写自定义逻辑处理代码,然后调用
    1. getWriterWithTempAndHandler()
    复制代码
    静态方法获取
    1. ExcelWriter
    复制代码
    对象时,传入
    1. WriterHandler
    复制代码
    的实现类即可。


    比如下面的示例代码:
      1. ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLSX, true, new MyWriterHandler());
      复制代码
    [h1]七、Web 下载示例代码[/h1]
      1. public class Down {
      复制代码
      1.     @GetMapping("/a.htm")
      复制代码
      1.     public void cooperation(HttpServletRequest request, HttpServletResponse response) {
      复制代码
      1.         ServletOutputStream out = response.getOutputStream();
      复制代码
      1.         ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, true);
      复制代码
      1.         String fileName = new String(("UserInfo " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
      复制代码
      1.                 .getBytes(), "UTF-8");
      复制代码
      1.         Sheet sheet1 = new Sheet(1, 0);
      复制代码
      1.         sheet1.setSheetName("第一个sheet");
      复制代码
      1.         writer.write0(getListString(), sheet1);
      复制代码
      1.         writer.finish();
      复制代码
      1.         response.setContentType("multipart/form-data");
      复制代码
      1.         response.setCharacterEncoding("utf-8");
      复制代码
      1.         response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
      复制代码
      1.         out.flush();
      复制代码
      1.         }
      复制代码
      1.     }
      复制代码
    [h1]八、需要注意的点[/h1][h2]8.1 写入大数据时,需分片[/h2]比如说,我们需要从数据库中查询出数据量较大时,我们需要在业务层做分片处理,也就是,我们需要分多次查询,再写入,防止内存溢出 OOM.
    [h2]8.2 Excel 最大行数问题[/h2]Excel 03, 07 版本均有行数、列数的限制:
    版本最大行最大列Excel 200365536256Excel 2007104857616384
    csv 由于是文本文件,实际上没有最大行数的限制,但是用 Excel 客户端打开还是多了不显示。
    也就是说,如果你想写入更多的行数是不行的,强行这么做,程序会报类似如下异常
      1. Invalid row number (1048576) outside allowable range (0..1048575)
      复制代码
    如何解决呢?
    • 分多个 Excel 文件写入;
    • 同一个 Excel 文件,分多个 Sheet 写入;
    [h1]九、总结[/h1]小哈今天主要给小伙伴介绍了 EasyExcel, 为什么要使用它,以及演示了相关示例代码。当然了,EasyExcel 除了写 Excel 文件外,它还有快速读取 Excel 的功能,由于本文主要介绍的是:如何优雅地实现 Excel 文件生成,所以就没有介绍了,有兴趣的小伙伴们,也可以去 GitHub 官网去去查看相关文档。
    最后,祝您看完本文后有所收获,下期见!
    [h1]十、GitHub 源码地址[/h1]https://github.com/weiwosuoai/spring-boot-tutorial/tree/master/spring-boot-excel
    [h1]十一、Ref[/h1]https://github.com/alibaba/easyexcel
    推荐阅读
    搞定SpringBoot2,我推荐你学习这个框架
    mybatis-plus支持分布式事务了?
    记住:永远不要在MySQL中使用UTF-8
    【珍藏】常用JVM命令参数
    利用策略模式优化过多 if else 代码
    程序员的黑砖窑,东南亚博彩骗局详解
    优雅的使用 ThreadLocal

    关注一下,尽知JAVA事!
    点鸭点鸭
    ↓↓↓↓
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:20
帖子:4
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP