驚了!7 行代碼優雅地實現 Excel 文件生成&下載功能

歡迎關注我的微信公衆號: 小哈學Javajava

我的網站: www.exception.site/essay/how-t…git

目錄

  • 1、前言github

  • 2、Apache poi、jxl 的缺陷面試

  • 3、阿里出品的 EasyExcel,安利一波spring

  • 4、EasyExcel 解決了什麼數據庫

  • 5、快速上手編程

  • 6、特殊場景支持bash

  • 7、Web 下載示例代碼微信

  • 8、須要注意的點併發

  • 9、總結

1、前言

關於導出 Excel 文件,能夠說是大多數服務中都須要集成的功能。那麼,要如何優雅快速地(偷懶地)去實現這個功能呢?

你可能第一想法是:這還不簡單?用 Apache 開源框架 poi, 或者 jxl 均可以實現啊。面向百度編程,把代碼模板 copy 下來,根據本身的業務再改改,能有多難?

你彷彿在逗我笑
你彷彿在逗我笑

嗯.. 的確不難,可是你的代碼多是下面這個熊樣子的:

用傳統excel框架生成文件的代碼模板
用傳統excel框架生成文件的代碼模板

上面這段代碼看上去是否是又臭又長呢?今天,小哈將教您如何使用 7 行代碼搞定 Excel 文件生成功能!

我要開始裝逼了
我要開始裝逼了

2、Apache poi、jxl 的缺陷

在說如何實現以前,咱們先來討論一下傳統 Excel 框架的不足!除了上面說的,Apache poi、jxl 都存在生成 excel 文件不夠簡單優雅快速外,它們都還存在一個嚴重的問題,那就是很是耗內存嚴重時會致使內存溢出

POI 雖然目前來講,是 excel 解析框架中被使用最普遍的,但這個框架並不完美。

爲何這麼說呢?

開發者們大部分使用 POI,都是使用其 userModel 模式。而 userModel 的好處是上手容易使用簡單,隨便拷貝個代碼跑一下,剩下就是寫業務轉換了,雖然轉換也要寫上百行代碼,可是仍是可控的。

然而 userModel 模式最大的問題是在於,對內存消耗很是大,一個幾兆的文件解析甚至要用掉上百兆的內存。現實狀況是,不少應用如今都在採用這種模式,之因此還正常在跑是由於併發不大,併發上來後,必定會OOM或者頻繁的 full gc。

3、阿里出品的 EasyExcel,安利一波

什麼是 EasyExcel? 見名知意,就是讓你操做 Excel 異常的酸爽。先來看下 EasyExcel GitHub 官方截圖:

easyExcel GitHub 截圖
easyExcel GitHub 截圖

截止目前爲止已有 5519 Star, 官方對其的簡介是:

快速、簡單避免OOM的java處理Excel工具!

如下是官方介紹:

esayExcel GitHub 簡介
esayExcel GitHub 簡介

4、EasyExcel 解決了什麼

主要來講,有如下幾點:

  • 傳統 Excel 框架,如 Apache poi、jxl 都存在內存溢出的問題;
  • 傳統 excel 開源框架使用複雜、繁瑣;
  • EasyExcel 底層仍是使用的 poi, 可是作了不少優化,好比修復了併發狀況下的一些 bug, 具體修復細節,可閱讀官方文檔 github.com/alibaba/eas…

好像很厲害的樣子
好像很厲害的樣子

5、快速上手

5.1 添加依賴

<!--alibaba easyexcel-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>easyexcel</artifactId>
  <version>1.1.2-beta5</version>
</dependency>
複製代碼

5.2 七行代碼搞定 Excel 生成

使用EasyExcel生成Excel模板代碼
使用EasyExcel生成Excel模板代碼

@Test
public void writeExcel1() throws Exception {
  // 文件輸出位置
  OutputStream out = new FileOutputStream("/Users/a123123/Work/tmp_files/test.xlsx");

  ExcelWriter writer = EasyExcelFactory.getWriter(out);

  // 寫僅有一個 Sheet 的 Excel 文件, 此場景較爲通用
  Sheet sheet1 = new Sheet(1, 0, WriteModel.class);

  // 第一個 sheet 名稱
  sheet1.setSheetName("第一個sheet");

  // 寫數據到 Writer 上下文中
  // 入參1: 建立要寫入的模型數據
  // 入參2: 要寫入的目標 sheet
  writer.write(createModelList(), sheet1);

  // 將上下文中的最終 outputStream 寫入到指定文件中
  writer.finish();

  // 關閉流
  out.close();
}
複製代碼

上面這段示例代碼中,有兩個點很重要,小哈已經重點標註標:

  • :WriteModel 這個對象就是要寫入 Excel 的數據模型對象,**等等,你這好像不行吧?表頭 head,以及每一個單元格內的數據順序都沒指定,能達到想要的效果麼?別急,後面會討論這塊!
  • :建立須要寫入的數據集,固然了,正常業務中,這塊都是從數據庫中查詢出來的。

PS: 若是說寫入的數據量很大,須要作分片查詢再寫入的處理,不然可能會 OOM(Out of Memory).

回過頭來,咱們來看看 WriteModel 這個對象內部到底有什麼幺蛾子!

WriteModel對象
WriteModel對象

/** * @author 微信公衆號: 小哈學Java * @Site: www.exception.site * @date 2019/5/9 * @time 下午2:07 * @discription 寫入Excel模型對象 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class WriteModel extends BaseRowModel {

    @ExcelProperty(value = "姓名", index = 0)
    private String name;

    @ExcelProperty(value = "密碼", index = 1)
    private String password;

    @ExcelProperty(value = "年齡", index = 2)
    private Integer age;
}
複製代碼

ExayExcel 提供註解的方式, 來方便的定義 Excel 須要的數據模型:

  • :首先,定義的寫入模型必需要繼承自 BaseRowModel.java;
  • :經過 @ExcelProperty 註解來指定每一個字段的列名稱,以及下標位置

同時,上面定義的 createModelList() 方法也很簡單,經過循環,建立一個寫入模型的 List 集合:

createModleList()
createModleList()

廢話很少說,這個快速接入的案例也介紹的差很少了,跑一跑單元測試看下實際效果:

測試結果
測試結果

怎麼樣,效果仍是挺棒棒的!

厲害了
厲害了

6、特殊場景支持

在實際的業務中,咱們還會有一些特需的需求,好比說下面這些。

6.1 動態生成 Excel 內容

上面的例子是基於註解的,也就是說表頭 head, 以及內容都是寫死的,換句話說,我定義好了一個數據模型,那麼,生成的 Excel 文件也就是隻能遵循這種模型來了,可是,實際業務中可能會存在動態變化的需求,要怎麼作呢?

動態生成Excel
動態生成Excel

@Test
public void writeExcel2() throws Exception {
  // 文件輸出位置
  OutputStream out = new FileOutputStream("/Users/a123123/Work/tmp_files/test2.xlsx");

  ExcelWriter writer = EasyExcelFactory.getWriter(out);

  // 動態添加表頭,適用一些表頭動態變化的場景
  Sheet sheet1 = new Sheet(1, 0);

  sheet1.setSheetName("第一個sheet");

  // 建立一個表格,用於 Sheet 中使用
  Table table1 = new Table(1);

  // 無註解的模式,動態添加表頭
  table1.setHead(DataUtil.createTestListStringHead());
  // 寫數據
  writer.write1(createDynamicModelList(), sheet1, table1);

  // 將上下文中的最終 outputStream 寫入到指定文件中
  writer.finish();

  // 關閉流
  out.close();
}
複製代碼
  • :無註解模式,動態添加表頭,也可自由組合複雜表頭,代碼以下:

動態生成Excel表頭數據
動態生成Excel表頭數據

public static List<List<String>> createTestListStringHead(){
    // 模型上沒有註解,表頭數據動態傳入
    List<List<String>> head = new ArrayList<List<String>>();
    List<String> headCoulumn1 = new ArrayList<String>();
    List<String> headCoulumn2 = new ArrayList<String>();
    List<String> headCoulumn3 = new ArrayList<String>();
    List<String> headCoulumn4 = new ArrayList<String>();
    List<String> headCoulumn5 = new ArrayList<String>();

    headCoulumn1.add("第一列");headCoulumn1.add("第一列");headCoulumn1.add("第一列");
    headCoulumn2.add("第一列");headCoulumn2.add("第一列");headCoulumn2.add("第一列");

    headCoulumn3.add("第二列");headCoulumn3.add("第二列");headCoulumn3.add("第二列");
    headCoulumn4.add("第三列");headCoulumn4.add("第三列2");headCoulumn4.add("第三列2");
    headCoulumn5.add("第一列");headCoulumn5.add("第3列");headCoulumn5.add("第4列");

    head.add(headCoulumn1);
    head.add(headCoulumn2);
    head.add(headCoulumn3);
    head.add(headCoulumn4);
    head.add(headCoulumn5);
    return head;
}
複製代碼
  • :建立動態數據,注意這裏的數據類型是 Object:

建立動態數據
建立動態數據

跑一下單元測試,看下效果:

動態建立效果測試
動態建立效果測試

6.2 自定義表頭以及內容樣式

我想自定義表頭,內容樣式,咋辦?

自定義表格樣式
自定義表格樣式

咱們複用了上面的示例代碼,並額外添加了設置自定義表格樣式的代碼, createTableStytle() 具體內容以下:

建立表格樣式代碼
建立表格樣式代碼

public static TableStyle createTableStyle() {
    TableStyle tableStyle = new TableStyle();
    // 設置表頭樣式
    Font headFont = new Font();
    // 字體是否加粗
    headFont.setBold(true);
    // 字體大小
    headFont.setFontHeightInPoints((short)12);
    // 字體
    headFont.setFontName("楷體");
    tableStyle.setTableHeadFont(headFont);
    // 背景色
    tableStyle.setTableHeadBackGroundColor(IndexedColors.BLUE);


    // 設置表格主體樣式
    Font contentFont = new Font();
    contentFont.setBold(true);
    contentFont.setFontHeightInPoints((short)12);
    contentFont.setFontName("黑體");
    tableStyle.setTableContentFont(contentFont);
    tableStyle.setTableContentBackGroundColor(IndexedColors.GREEN);
    return tableStyle;
}
複製代碼

咱們能夠經過 TableStyle 這個類來設置表頭、表格主題的樣式。

6.3 合併單元格

咱們能夠經過 merge() 方法來合併單元格:

合併單元格
合併單元格

注意下標是從 0 開始的,也就是說合並了第六行到第七行,其中的第一列到第五列,跑下代碼,看下效果:

合併單元格效果圖
合併單元格效果圖

6.4 自定義處理

對於更復雜的處理,EasyExcel 預留了 WriterHandler 接口來,容許你自定義處理代碼:

WriterHandler
WriterHandler

接口中定義了三個方法:

  • sheet(): 在建立每一個 sheet 後自定義業務邏輯處理;
  • row(): 在建立每一個 row 後自定義業務邏輯處理;
  • cell(): 在建立每一個 cell 後自定義業務邏輯處理;

咱們實現了該接口後,編寫自定義邏輯處理代碼,而後調用 getWriterWithTempAndHandler() 靜態方法獲取 ExcelWriter 對象時,傳入 WriterHandler 的實現類便可。

傳入WriterHandler
傳入WriterHandler

好比下面的示例代碼:

ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLSX, true, new MyWriterHandler());
複製代碼

7、Web 下載示例代碼

public class Down {
    @GetMapping("/a.htm")
    public void cooperation(HttpServletRequest request, HttpServletResponse response) {
        ServletOutputStream out = response.getOutputStream();
        ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, true);
        String fileName = new String(("UserInfo " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
                .getBytes(), "UTF-8");
        Sheet sheet1 = new Sheet(1, 0);
        sheet1.setSheetName("第一個sheet");
        writer.write0(getListString(), sheet1);
        writer.finish();
        response.setContentType("multipart/form-data");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
        out.flush();
        }
    }
複製代碼

8、須要注意的點

8.1 寫入大數據時,需分片

好比說,咱們須要從數據庫中查詢出數據量較大時,咱們須要在業務層作分片處理,也就是,咱們須要分屢次查詢,再寫入,防止內存溢出 OOM.

8.2 Excel 最大行數問題

Excel 03, 07 版本均有行數、列數的限制:

版本 最大行 最大列
Excel 2003 65536 256
Excel 2007 1048576 16384

csv 因爲是文本文件,實際上沒有最大行數的限制,可是用 Excel 客戶端打開仍是多了不顯示。

也就是說,若是你想寫入更多的行數是不行的,強行這麼作,程序會報相似以下異常

Invalid row number (1048576) outside allowable range (0..1048575)
複製代碼

如何解決呢?

  1. 分多個 Excel 文件寫入;
  2. 同一個 Excel 文件,分多個 Sheet 寫入;

9、總結

小哈今天主要給小夥伴介紹了 EasyExcel, 爲何要使用它,以及演示了相關示例代碼。固然了,EasyExcel 除了寫 Excel 文件外,它還有快速讀取 Excel 的功能,因爲本文主要介紹的是:如何優雅地實現 Excel 文件生成,因此就沒有介紹了,有興趣的小夥伴們,也能夠去 GitHub 官網去去查看相關文檔。

最後,祝您看完本文後有所收穫,下期見!

10、GitHub 源碼地址

github.com/weiwosuoai/…

11、Ref

github.com/alibaba/eas…

免費分享 | 面試&學習福利資源

最近在網上發現一個不錯的 PDF 資源《Java 核心知識&面試.pdf》分享給你們,不光是面試,學習,你都值得擁有!!!

獲取方式: 關注公衆號: 小哈學Java, 後臺回覆資源,既可免費無套路獲取資源連接,下面是目錄以及部分截圖:

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦

重要的事情說兩遍,關注公衆號: 小哈學Java, 後臺回覆資源,既可免費無套路獲取資源連接 !!!

歡迎關注微信公衆號: 小哈學Java

關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
關注微信公衆號【小哈學Java】,回覆【資源】,便可免費無套路領取資源連接哦
相關文章
相關標籤/搜索