新一代 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 的用法介紹

相關文章
相關標籤/搜索