EasyExcel寫入百萬級數據到多sheet---非註解方式

EasyExcel是什麼?java

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

1、項目需求github

       從mongo庫中查詢數據,導出到excel文件中。可是動態導出的excel有多少列、列名是什麼、有多少sheet頁都須要動態獲取。因此生成的excel也必須是動態生成,不能經過註解配置對象映射。並且寫入的數據量,有可能達到100W級,使用傳統的POI工具,須要把excel數據所有加載到內存空間,內存空間很容易OOM。因此選擇了阿里的EasyExcel,聽說能夠高效的解決POI的OOM問題。apache

2、測試Demosegmentfault

   一、引入的pom依賴微信

    

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.1.2-beta5</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>

 

   二、測試代碼jvm

package com.movitech.product.datahub.util; import com.alibaba.excel.EasyExcelFactory; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.WriteHandler; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Table; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.ExcelBuilderImpl; import org.apache.poi.ss.usermodel.*; import java.io.*; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * @Author JAY * @Date 2019/8/29 11:00 * @Description TODO **/ public class EasyExcelUtil { public static String excelFilePath = "C:\\Users\\lenovo\\Desktop\\Jay01-(jay01)-v5自定義導入數據.xls"; public static void main(String[] args) { try { writeExcel(excelFilePath); } catch (IOException e) { e.printStackTrace(); } } public static void writeExcel(String excelFile) throws IOException { // 文件輸出位置 OutputStream out = new FileOutputStream(excelFile); ExcelWriter writer = EasyExcelFactory.getWriter(out); // 動態添加表頭,適用一些表頭動態變化的場景 Sheet sheet1 = new Sheet(1, 0); sheet1.setSheetName("第一個sheet"); // 建立一個表格,用於 Sheet 中使用 Table table1 = new Table(1); // 無註解的模式,動態添加表頭  table1.setHead(createTestListStringHead()); // 寫數據 writer.write1(new ArrayList<>(), sheet1, table1); // 動態添加表頭,適用一些表頭動態變化的場景 Sheet sheet2 = new Sheet(2, 0); sheet2.setSheetName("第2個sheet"); /* 添加TableStyle屬性會使內存OOM,沒辦法知足分批插入100W條數據 TableStyle tableStyle = new TableStyle(); com.alibaba.excel.metadata.Font font = new com.alibaba.excel.metadata.Font(); font.setBold(true); tableStyle.setTableContentFont(font); sheet2.setTableStyle(tableStyle); */ // 建立一個表格,用於 Sheet 中使用 Table table2 = new Table(2); // 無註解的模式,動態添加表頭  table2.setHead(createTestListStringHead()); writer.write1(new ArrayList<>(), sheet2, table2); int x = 0; while (x < 1000000) {
     // 模擬分批寫入數據到excel,每次寫入100條 System.out.println("x = " + x); Table tableX = new Table(1);

        // 每次從sheet的第幾行開始寫入 sheet1.setStartRow(x); writer.write1(createDynamicModelList(x), sheet1, tableX); Table tableX2 = new Table(1); sheet2.setStartRow(x); writer.write1(createDynamicModelList(x), sheet2, tableX2); x = x + 100; } // 將上下文中的最終 outputStream 寫入到指定文件中 writer.finish(); // 關閉流 out.close(); } private static List<List<Object>> createDynamicModelList(int x) { List<List<Object>> rows = new ArrayList<>(); for (int i= x; i < 100 + x; i++){ List<Object> row = new ArrayList<>(); row.add("字符串-" + i); row.add(Long.valueOf(187837834L) + i); row.add(Integer.valueOf(2233 + i)); row.add("寧-" + i); row.add("微信公衆號: demo"); rows.add(row); } return rows; } private 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("第1列"); headCoulumn2.add("第2列"); headCoulumn3.add("第3列"); headCoulumn4.add("第4列"); headCoulumn5.add("第5列"); head.add(headCoulumn1); head.add(headCoulumn2); head.add(headCoulumn3); head.add(headCoulumn4); head.add(headCoulumn5); return head; } }

   三、執行結果ide

 

 

 

總結:工具

 此測試代碼能夠直接運行測試查看結果。測試

我配置的jvm運行參數,

 

 我只給了10M空間,可是往excel中寫入100W數據,程序並無出現OOM。能夠看到,使用EasyExcel,確實解決了OOM問題。

可是實際狀況,EasyExcel不足以知足個人業務需求。由於除了百萬級的數據導出以外,還須要進行sheet頁隱藏、行隱藏、列隱藏等操做。目前EasyExcel的API,尚未那麼多的功能變化。不過,easyExcel提供了自定義攔截器的功能,貌似能夠給excel作樣式處理。大體測試了一下,能夠隱藏列和sheet,可是不知道怎麼隱藏行。測試代碼以下:

 (1)隱藏列,經過自定義攔截器

public static void writeExcelToSheet(String excelFile, Sheet sheet) throws IOException { // 文件輸出位置
        OutputStream out = new FileOutputStream(excelFile); ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLS, true, new WriteHandler() { @Override public void sheet(int i, org.apache.poi.ss.usermodel.Sheet sheet) { sheet.setColumnHidden(0,true); sheet.setColumnHidden(1,true); } @Override public void row(int i, Row row) { System.out.println("row : " + row.getRowNum()); } @Override public void cell(int i, Cell cell) { System.out.println("cell : " + i); } }); Table table1 = new Table(1); table1.setHead(createTestListStringHead());// 寫數據
        writer.write1(createDynamicModelList(0), sheet, table1); // 將上下文中的最終 outputStream 寫入到指定文件中
 writer.finish(); // 關閉流
 out.close(); }

  (2)隱藏sheet頁,經過反射獲取Workbook,用wb來設置隱藏sheet頁

/** * **獲取workbook** * 由於EasyExcel這個庫設計的緣由 * 只能使用反射獲取workbook * * @param writer * @return
     */
    private static Workbook getWorkbook(ExcelWriter writer) { Workbook workbook = null; try { Class<?> clazz1 = Class.forName("com.alibaba.excel.ExcelWriter"); Field[] fs = clazz1.getDeclaredFields(); for (Field field : fs) { // 要設置屬性可達,否則會拋出IllegalAccessException異常
                field.setAccessible(true); if ("excelBuilder".equals(field.getName())) { ExcelBuilderImpl excelBuilder = (ExcelBuilderImpl) field.get(writer); Class<?> clazz2 = Class.forName("com.alibaba.excel.write.ExcelBuilderImpl"); Field[] fs2 = clazz2.getDeclaredFields(); for (Field field2 : fs2) { field2.setAccessible(true); if ("context".equals(field2.getName())) { WriteContext context = (WriteContext) field2.get(excelBuilder); workbook = context.getWorkbook(); } } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return workbook; }

 

public static void writeExcel(String excelFile) throws IOException { // 文件輸出位置
        OutputStream out = new FileOutputStream(excelFile); ExcelWriter writer = EasyExcelFactory.getWriter(out); // 動態添加表頭,適用一些表頭動態變化的場景
        Sheet sheet1 = new Sheet(1, 0); sheet1.setSheetName("第一個sheet"); // 建立一個表格,用於 Sheet 中使用
        Table table1 = new Table(1); // 無註解的模式,動態添加表頭
 table1.setHead(createTestListStringHead()); // 寫數據
        writer.write1(new ArrayList<>(), sheet1, table1); // 動態添加表頭,適用一些表頭動態變化的場景
        Sheet sheet2 = new Sheet(2, 0); sheet2.setSheetName("第2個sheet"); /* 添加TableStyle屬性會使內存OOM TableStyle tableStyle = new TableStyle(); com.alibaba.excel.metadata.Font font = new com.alibaba.excel.metadata.Font(); font.setBold(true); tableStyle.setTableContentFont(font); sheet2.setTableStyle(tableStyle); */

        // 建立一個表格,用於 Sheet 中使用
        Table table2 = new Table(2); // 無註解的模式,動態添加表頭
 table2.setHead(createTestListStringHead()); writer.write1(new ArrayList<>(), sheet2, table2); int x = 0; while (x < 10000) { System.out.println("x = " + x); Table tableX = new Table(1); sheet1.setStartRow(x); writer.write1(createDynamicModelList(x), sheet1, tableX); Table tableX2 = new Table(1); sheet2.setStartRow(x); writer.write1(createDynamicModelList(x), sheet2, tableX2); x = x + 100; } //獲取workbook,隱藏第2頁sheet
      Workbook workbook = getWorkbook(writer); workbook.setSheetHidden(1,true); // 將上下文中的最終 outputStream 寫入到指定文件中
 writer.finish(); // 關閉流
 out.close(); }

 

 

 

參考資源 https://segmentfault.com/a/1190000019472781,https://github.com/alibaba/easyexcel

相關文章
相關標籤/搜索