新一代 Excel 导出工具:ExcelUtil + RunnerUtil 介绍

背景 —— 提一个好的问题

开发过程当中常常会遇到 Excel 导出的状况,尤为是在企业开发中,涉及到客户信息、财务报表、市场分析等,情景很是多。日常开发过程当中大多都会针对每一个导出单独写一套代码,随着导出愈来愈多,内心便想:有没有一个足够通用东西可让咱们不用写这么多代码来实现 Excel 导出?前端

带着这个问题便开始了本身的“ExcelUtil”之路,在这过程当中主要接触过 easypoi,但仍是不太知足。由于 easypoi 和大多数 Java 库同样:基于字段写配置。固然不是说这个很差,有不少库都这样,好比 fastjson、Jackson 等都是在字段上写注解,描述这个字段有些什么信息或做用等。但对于 Excel 导出,我总以为还有更加通用的方式。vue

通过一段时间的摸索和发掘,在前端的 table 标签上找到了灵感,认为这个方式很好、很是好。table 标签自己包含了不少描述信息,像行、列、合并行、合并列这些与 excel 的 sheet 页“惊人的类似”,再加上近几年前端三大框架的大力发展,尤为是 angular 和 vue 这两个框架在标签上自定义属性的方式进一步让我在写 ExcelUtil 过程当中获得了很多启发。java

简介

ExcelUtil 和 RunnerUtilGitHub) 同样,大概是在今年 5 到 6 月写的,最近又从新整理了一下,已上传 GitHub # ExcelUtil (记得 star 哈!)git

ExcelUtil 根据 excel 文件、sheet 页、row 行、cell 单元格这样的层次结构分别定义了本身的做用域,每一个做用域内能够必定程度上自定义变量等,做用域之间互不影响,同名变量下层做用域等声明优先于上层做用域等这些与 java、JavaScript 等语言的做用域结构一致。github

不一样的是 ExcelUtil 使用频率比 RunnerUtil 频率高不少,写 RunnerUtil 的初衷也是为了这个 ExcelUtil 导出,最开始想到了 Java 内置脚本引擎(ScriptEngine),但内置脚本引擎的效率实在过低,数据量稍微大一点(不用太大)状况下直接卡死(不应这么吐槽的,但的确不适合这个场景)。可是 RunnerUtil 的功能独立且完善,性能良好,能够运行各类复杂的 Java 字符串公式,彻底能够单独使用。面试

使用介绍

  1. 使用 ExcelUtil 的以前首先要准备的就是数据,数据并无特殊的格式要求,能够是任意 Java 类型数据,如 Collection、Iterable、Iterator(迭代器模式可,这是在一次面试时获得的启发,可用于超大 Excel 导出,虽而后来没经过,但仍然很感谢那位面试官!)、Map、数组、POJO、Number等。编程

  2. 第二步是生成 Workbook 位置的方法上进行“注解编程” —— 对的,Java 的注解功能很强大,能够在 Java 内部又单独做为 Java 内的“编程语言”(其实就是写了个简单的解析器而言,捂脸一笑)。json

// 在什么地方导出,就在那个方法上进行声明式“注解编程”
// 首先要声明这是一个 Excel,用 type 指定是 xls 或者 xlsx
@TableExcel(type = TableExcel.Type.XLS, value = {
    /* * value 包含的是全部 sheet 页的信息 * 自 sheet 向下,每一个标签能够判断、循环等 * 用 sheetName 指定 sheet 名 * 为何要用单引号再多包裹一层呢?详见 RunnerUtil * 由于这里面的全部内容都是用 RunnerUtil 解析的,须要符合它的格式 */ 
    @TableSheet(sheetName = "'人员信息'", value = {
        /* * 在这儿声明了一个名为 names 的数组,用做标题 */
        @TableRow(var = "names = {'序号','姓名','性别','年龄','电话','家庭住址', '备注'}", value = {
            /* * 这儿用了迭代,迭代 row 上声明的 names * 这个迭代将按 names 的内容生成对应数量和内容的 cell 单元格 */
            @TableCell(var = "name:names", value = name)
        }),
        /* * 上面 cell 的迭代用的是冒号,这儿用了 in,两者意义彻底同样 * 支持 in 彻底是为了向灵感的来源(前端)致敬 * 可是 in 并非关键字,仍可做为普通变量 * 不一样的是 in 的两端至少各有一个空格 * 可迭代的数据类型一下子详细介绍 */
        @TableRow(var = "($rowData, index) in collect", value = {
            @TableCell("index + 1"), // 序号
            @TableCell("$rowData.name"), // 姓名
            @TableCell("$rowData.sex"), // 性别
            @TableCell("$rowData.age"), // 年龄
            @TableCell("$rowData.mobile"), // 电话
            @TableCell("$rowData.address"), // 家庭住址
            // 最后这个对于上面的备注,这儿有个 when,只有 index == 0 才建立这个单元格
            // 同时这儿还用到了并合并行,另外 colspan 是合并列
            @TableCell(when = "index == 0", rowspan = "data.size()")
        })
    })
})
public Workbook exportExcel(Object data){
    /* * 写好注解后只须要调用这个方法即可获得一个 Workbook * 在哪儿调用 render 方法就在哪儿写上面那些注解 */
    return ExcelUtil.render(data);
}
复制代码
  • ExcelUtil.render(data); 在渲染中 in (或冒号 :)可迭代的数据有:
  1. number(整数),如 var = "$item in 10",循环十次;
  2. 字符串,迭代出字符串中的每一个字符,但因为 RunnerUtil 是不支持 char 类型数据的,因此实际上迭代出来的是单个字符的字符串
  3. Collection、Iterable、List、Set 等集合。
  4. Map,迭代出来的是每个键值对的值;
  5. POJO,普通 Java 对象按字段名迭代,迭代出的是字段值
  • when 后面的表达式返回值必须是 boolean 类型
  • colspan、rowspan 表达式返回值必须是 int 类型
  • 其余的还有 heigit、width 等也必须是 int 类型

使用效果:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/11/28/16758316e9699b4d~tplv-t2oaga2asx-image.image

  • 生成的对应 Excel 效果图

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/11/28/16758316e9782ec1~tplv-t2oaga2asx-image.image

性能测试

贴一个本工具导出的 10 列 Excel 的性能测试表(本机环境 i7-8700K 16G Win10)数组

行数(万行) 生成数据耗时(ms) write到文件耗时(ms) 总耗时(ms)
100 6,182 5,565 11,747
300 14,800 16,693 31,493
500 25,876 27,317 53,193
700 36,121 42,171 78,292
999 53,532 54,745 108,277
4000 240,453 271,832 512,285
6000 366,987 423,351 790,338
8000 528,654 498,490 1,027,144

从这个数据能够看出,随着数据量增长,时间与数据的关系呈正相关性,接近线性关系,100 万行数据生成 Workbook 耗时 6s,总耗时 12s,在正常业务场景下能知足时间的要求。markdown

其余说明

  • 当 Excel 数据量超过 150 万行时,不建议用 xls 格式(这个数据在不一样机器上应该有差别,本机 150 万行的 xls 能正常导出,180 万行就 OOM 了);
  • 当数据量超过 500 万行时(随环境而异),TableExcel 的 type 值应为 SUPER(type = TableExcel.Type.SUPER),SUPER 对应的也是 xlsx 格式,可是 SUPER 是用来支持超大数据导出的;
  • 150 万行和 500 万行基本是正常业务极限值了。
  • 目前只支持导出,还不能导入。

ExcelUtil # GitHub # star ~!

RunnerUtil 的用法介绍

相关文章
相关标签/搜索