国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

java 導出 excel 最佳實踐,java 大文件 excel 避免OOM(內存溢出) exce

K_B_Z / 2071人閱讀

摘要:消費之后,多線程處理文件導出,生成文件后上傳到等文件服務器。前端直接查詢并且展現對應的任務執行列表,去等文件服務器下載文件即可。這客戶體驗不友好,而且網絡傳輸,系統占用多種問題。拓展閱讀導出最佳實踐框架

產品需求

產品經理需要導出一個頁面的所有的信息到 EXCEL 文件。

需求分析

對于 excel 導出,是一個很常見的需求。

最常見的解決方案就是使用 poi 直接同步導出一個 excel 文件。

客戶體驗 & 服務性能

客戶體驗

如果導出的文件比較大,比如幾十萬條數據,同步導出頁面就會卡主,用戶無法進行其他操作。

服務性能

導出的時候,任務比較耗時就會阻塞主線程。

如果導出的服務是暴露給外部(前后端分離),這種大量的數據傳輸十分消耗性能。

解決方案

使用異常處理導出請求,后臺 MQ 通知自己進行處理。

MQ 消費之后,多線程處理 excel 文件導出,生成文件后上傳到 FTP 等文件服務器。

前端直接查詢并且展現對應的任務執行列表,去 FTP 等文件服務器下載文件即可。

EXCEL 導出需要考慮的問題 OOM

正常的 poi 在處理比較大的 excel 的時候,會出現內存溢出。

網上的解決方案也比較多。

比如官方的 SXSSF (Since POI 3.8 beta3) 解決方式。

或者使用封裝好的包

easypoi ExcelBatchExportServer

hutool BigExcelWriter

原理都是強制使用 xssf 版本的Excel

你也可以使用 easyexcel,當然這個注釋文檔有些欠缺,而且設計的比較復雜,不是很推薦。

我這里使用的是 hutool BigExcelWriter,
懶得自己再寫一遍。

FULL GC

如果一次查詢 100W 條數據庫,然后把這些信息全部加載到內存中,是不可取的。

建議有2個:

限制每一次分頁的數量。比如一次最多查詢 1w 條。分成 100 次查詢。(必須)

限制查詢得總條數。比如限制為最多 10W 條。(根據實際情況選擇)

雖然使用者提出要導出類似于 3 個月的所有信息,但是數量太多,毫無意義。(提出者自己可能體會不到)

盡量避免 FULL-GC 的情況發生,因為目前的所有方式對于 excel 的輸出流都會占用內存,100W 條很容易導致 FULL-GC。

數據庫的壓力

去數據庫讀取的時候一定要記得分頁,免得給數據庫太大的壓力。

一次讀取太多,也會導致內存直線上升。

比如 100W 條數據,則分成 100 次去數據庫讀取。

網絡傳輸

傳統的 excel 導出,都是前端一個請求,直接 HTTP 同步返回。導出 100W 條,就在那里傻等。

這客戶體驗不友好,而且網絡傳輸,系統占用多種問題。

建議使用異步處理的方式,將文件上傳到文件服務器。前端直接去文件服務器讀取。

編程的便利性

對于上面提到的工具,比如 Hutool,在表頭的處理方面沒法很方便的統一。

你可以自己定義類似于 easypoi/easyexcel 中的注解,自己反射解析。

然后統一處理表頭即可。

IExcel 方便優雅的 excel 框架 特性

OO 的方式操作 excel,編程更加方便優雅。

sax 模式讀取,SXSS 模式寫入。避免 excel 大文件 OOM。

基于注解,編程更加靈活。

寫入可以基于對象列表,也可以基于 Map,實際使用更加方便。

設計簡單,注釋完整。方便大家學習改造。

后期特性

讀取跳過空白行

excel 樣式相關的注解開發

創作緣由

實際工作和學習中,apache poi 操作 excel 過于復雜。

近期也看了一些其他的工具框架:

easypoi

easyexcel

hutool-poi

都或多或少難以滿足自己的實際需要,于是就自己寫了一個操作 excel 導出的工具。

快速開始 引入 Jar

使用 maven 管理。


     com.github.houbb
     iexcel
     0.0.2
定義對象

你可以直接參考 ExcelUtilTest.java

定義一個需要寫入/讀取的 excel 對象。

ExcelFieldModel.java

只有聲明了 @ExcelField 的屬性才會被處理,使用說明:@ExcelField

public class ExcelFieldModel {

    @ExcelField
    private String name;

    @ExcelField(headName = "年齡")
    private String age;

    @ExcelField(mapKey = "EMAIL", writeRequire = false, readRequire = false)
    private String email;

    @ExcelField(mapKey = "ADDRESS", headName = "地址", writeRequire = true)
    private String address;
    
    //getter and setter
}
寫入例子 IExcelWriter 的實現

IExcelWriter 有幾個實現類,你可以直接 new 或者借助 ExcelUtil 類去創建。

IExcelWriter 實現類 ExcelUtil 如何創建 說明
HSSFExcelWriter ExcelUtil.get03ExcelWriter() 2003 版本的 excel
XSSFExcelWriter ExcelUtil.get07ExcelWriter() 2007 版本的 excel
SXSSFExcelWriter ExcelUtil.getBigExcelWriter() 大文件 excel,避免 OOM
IExcelWriter 接口說明
寫入到 2003

excelWriter03Test()

一個將對象列表寫入 2003 excel 文件的例子。

/**
 * 寫入到 03 excel 文件
 */
@Test
public void excelWriter03Test() {
    // 待生成的 excel 文件路徑
    final String filePath = "excelWriter03.xls";

    // 對象列表
    List models = buildModelList();

    try(IExcelWriter excelWriter = ExcelUtil.get03ExcelWriter();
        OutputStream outputStream = new FileOutputStream(filePath)) {
        // 可根據實際需要,多次寫入列表
        excelWriter.write(models);

        // 將列表內容真正的輸出到 excel 文件
        excelWriter.flush(outputStream);
    } catch (IOException e) {
        throw new ExcelRuntimeException(e);
    }
}

buildModelList()

/**
 * 構建測試的對象列表
 * @return 對象列表
 */
private List buildModelList() {
    List models = new ArrayList<>();
    ExcelFieldModel model = new ExcelFieldModel();
    model.setName("測試1號");
    model.setAge("25");
    model.setEmail("123@gmail.com");
    model.setAddress("貝克街23號");

    ExcelFieldModel modelTwo = new ExcelFieldModel();
    modelTwo.setName("測試2號");
    modelTwo.setAge("30");
    modelTwo.setEmail("125@gmail.com");
    modelTwo.setAddress("貝克街26號");

    models.add(model);
    models.add(modelTwo);
    return models;
}
一次性寫入到 2007 excel

有時候列表只寫入一次很常見,所有就簡單的封裝了下:

/**
 * 只寫入一次列表
 * 其實是對原來方法的簡單封裝
 */
@Test
public void onceWriterAndFlush07Test() {
    // 待生成的 excel 文件路徑
    final String filePath = "onceWriterAndFlush07.xlsx";

    // 對象列表
    List models = buildModelList();

    // 對應的 excel 寫入對象
    IExcelWriter excelWriter = ExcelUtil.get07ExcelWriter();

    // 只寫入一次列表
    ExcelUtil.onceWriteAndFlush(excelWriter, models, filePath);
}
讀取例子

excel 讀取時會根據文件名稱判斷是哪個版本的 excel。

IExcelReader 的實現

IExcelReader 有幾個實現類,你可以直接 new 或者借助 ExcelUtil 類去創建。

IExcelReader 實現類 ExcelUtil 如何創建 說明
ExcelReader ExcelUtil.getExcelReader() 小文件的 excel 讀取實現
Sax03ExcelReader ExcelUtil.getBigExcelReader() 大文件的 2003 excel 讀取實現
Sax07ExcelReader ExcelUtil.getBigExcelReader() 大文件的 2007 excel 讀取實現
IExcelReader 接口說明
excel 讀取的例子
/**
 * 讀取測試
 */
@Test
public void readWriterTest() {
    File file = new File("excelWriter03.xls");
    IExcelReader excelReader = ExcelUtil.getExcelReader(file);
    List models = excelReader.readAll(ExcelFieldModel.class);
    System.out.println(models);
}
ExcelField 注解說明

@ExcelField 的屬性說明如下:

屬性 類型 默認值 說明
mapKey String "" 僅用于生成的入參為 map 時,會將 map.key 對應的值映射到 bean 上。如果不傳:默認使用當前字段名稱
headName String "" excel 表頭字段名稱,如果不傳:默認使用當前字段名稱
writeRequire boolean true excel 文件是否需要寫入此字段
readRequire boolean true excel 文件是否讀取此字段
IExcelWriter 接口說明
/**
 * 寫出數據,本方法只是將數據寫入Workbook中的Sheet,并不寫出到文件
*

* data中元素支持的類型有: *

 * 1. Bean,既元素為一個Bean,第一個Bean的字段名列表會作為首行,剩下的行為Bean的字段值列表,data表示多行 
*
* @param data 數據 * @return this */ IExcelWriter write(Collection data); /** * 寫出數據,本方法只是將數據寫入Workbook中的Sheet,并不寫出到文件
* 將 map 按照 targetClass 轉換為對象列表 * 應用場景: 直接 mybatis mapper 查詢出的 map 結果,或者其他的構造結果。 * @param mapList map 集合 * @param targetClass 目標類型 * @return this */ IExcelWriter write(Collection> mapList, final Class targetClass); /** * 將Excel Workbook刷出到輸出流 * * @param outputStream 輸出流 * @return this */ IExcelWriter flush(OutputStream outputStream);
指定 sheet

創建 IExcelWriter 的時候,可以指定 sheet 的下標或者名稱。來指定寫入的 sheet。

是否包含表頭

創建 IExcelWriter 的后,可以調用 excelWriter.containsHead(bool) 指定是否生成 excel 表頭。

IExcelReader 接口說明
/**
 * 讀取當前 sheet 的所有信息
 * @param tClass 對應的 javabean 類型
 * @return 對象列表
 */
List readAll(Class tClass);

/**
 * 讀取指定范圍內的
 * @param tClass 泛型
 * @param startIndex 開始的行信息(從0開始)
 * @param endIndex 結束的行信息
 * @return 讀取的對象列表
 */
List read(Class tClass, final int startIndex, final int endIndex);
指定 sheet

創建 IExcelReader 的時候,可以指定 sheet 的下標或者名稱。來指定讀取的 sheet。

注意:大文件 sax 讀取模式,只支持指定 sheet 的下標。

是否包含表頭

創建 IExcelReader 的后,可以調用 excelReader.containsHead(bool) 指定是否讀取 excel 表頭。

拓展閱讀

excel 導出最佳實踐

iexcel 框架

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72442.html

相關文章

  • Excel批量數據的導入和導出,如何做優化?

    摘要:并且在對的抽象中,每一行,每一個單元格都是一個對象。對支持使用官方例子需要繼承,覆蓋方法,每讀取到一個單元格的數據則會回調次方法。概要Java對Excel的操作一般都是用POI,但是數據量大的話可能會導致頻繁的FGC或OOM,這篇文章跟大家說下如果避免踩POI的坑,以及分別對于xls和xlsx文件怎么優化大批量數據的導入和導出。一次線上問題這是一次線上的問題,因為一個大數據量的Excel導出...

    Tecode 評論0 收藏0
  • POI讀取文件最佳實踐

    摘要:我最近做的一個工具就是讀取計算機中的以及文件。經常在讀取某些特別大的文件的時候都會帶來一個內存溢出的問題。以上,就是我在使用讀取文件的一些探索和發現,希望對你能有所幫助。 POI是 Apache 旗下一款讀寫微軟家文檔聲名顯赫的類庫。應該很多人在做報表的導出,或者創建 word 文檔以及讀取之類的都是用過 POI。POI 也的確對于這些操作帶來很大的便利性。我最近做的一個工具就是讀取計...

    bingchen 評論0 收藏0
  • Java開發

    摘要:大多數待遇豐厚的開發職位都要求開發者精通多線程技術并且有豐富的程序開發調試優化經驗,所以線程相關的問題在面試中經常會被提到。將對象編碼為字節流稱之為序列化,反之將字節流重建成對象稱之為反序列化。 JVM 內存溢出實例 - 實戰 JVM(二) 介紹 JVM 內存溢出產生情況分析 Java - 注解詳解 詳細介紹 Java 注解的使用,有利于學習編譯時注解 Java 程序員快速上手 Kot...

    LuDongWei 評論0 收藏0
  • PHP實時生成并下載超數據量的EXCEL文件

    摘要:而常用的包需要把所有數據拿到后才能生成,在面對生成超大數據量的文件時這顯然是會造成內存溢出的,所以考慮使用讓邊寫入輸出流邊讓瀏覽器下載的形式來完成需求。 最近接到一個需求,通過選擇的時間段導出對應的用戶訪問日志到excel中, 由于用戶量較大,經常會有導出50萬加數據的情況。而常用的PHPexcel包需要把所有數據拿到后才能生成excel, 在面對生成超大數據量的excel文件時這顯然...

    Neilyo 評論0 收藏0

發表評論

0條評論

K_B_Z

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<