文章目錄
- 讀的流程圖
- 邏輯分析
- 1.EasyExcel
- 2.EasyExcelFactory
- ExcelReaderSheetBuilder
- ExcelReader
- ExcelAnalyserImpl
- XlsxSaxAnalyser
- DefaultAnalysisEventProcessor
- DemoDatalistener的invoke()方法被調用
- 傳回到XlsxSaxAnalyser
- DefaultAnalysisEventProcessor的endSheet()
- DemoDataistener的doAfterAllAnalysed()
- 備注:官方給的詳細參數介紹
- 關于常見類解析
- 讀
- 注解
- 參數
- 通用參數
- ReadWorkbook(了解成excel對象)參數
- ReadSheet(就是excel的一個Sheet)參數
- 寫
- 注解
- 參數
- 通用參數
- WriteWorkbook(了解成excel對象)參數
- WriteSheet(就是excel的一個Sheet)參數
- WriteTable(就把excel的一個Sheet,一塊區域看一個table)參數
- 10M以上檔案讀取說明
- 如果對讀取效率感覺還能接受,就用預設的,永久占用(單個excel讀取整個過程)一般不會超過50M(大機率就30M),剩下臨時的GC會很快回收
- 預設大檔案處理
- 根據實際需求配置記憶體
- 如果最大檔案條數也就十幾二十萬,然後excel也就是十幾二十M,而且不會有很高的并發,并且記憶體也較大
- 對并發要求較高,而且都是經常有超級大檔案
- 關于maxCacheActivateSize 也就是前面第二個參數的詳細說明
- 如何判斷 maxCacheActivateSize是否需要調整
最近公司用到easyexcel,跟了下源碼,簡單了解的部分流程。做一下筆記,友善以後查詢。
本人主要根據官方給的最簡單的read demo的調試下了:
讀的流程圖
流程圖大體如下:
- 讀的代碼
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 這裡千萬别忘記關閉,讀的時候會建立臨時檔案,到時磁盤會崩的
excelReader.finish();
邏輯分析
1.EasyExcel
easyexcel就是一個門面他是一個空的類,主要幹活的是他的父類:
public class EasyExcel extends EasyExcelFactory {
}
2.EasyExcelFactory
作用: ExcelWriterBuilder 建構出一個 ReadWorkbook WriteWorkbook,可以了解成一個excel對象,一個excel隻要建構.
繼續看這行代碼: EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
進入read:
public static ExcelReaderBuilder read(String pathName, Class head, ReadListener readListener) {
ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder();
excelReaderBuilder.file(pathName);
if (head != null) {
excelReaderBuilder.head(head);
}
if (readListener != null) {
excelReaderBuilder.registerReadListener(readListener);
}
return excelReaderBuilder;
}
主要幹了以下幾件事:
- 1.初始化了
這個對象:ExcelReaderBuilder
- 2将Demo.class對應的标題頭資訊放入
這個類相關子中。BasicParameter
- 3将Demo.controller對應的标題頭資訊放入
這個類中。ReadBasicParameter
ExcelReaderSheetBuilder
繼續看這行代碼:
ReadSheet readSheet = EasyExcel.readSheet(0).build();
幹了這件事:
public static ExcelReaderSheetBuilder readSheet(Integer sheetNo) {
return readSheet(sheetNo, null);
}
ExcelWriterSheetBuilder 建構出一個 ReadSheet WriteSheet對象,可以了解成excel裡面的一頁,每一頁都要建構一個.
ExcelReader
這個類是整個個讀的入口類:
看這個代碼
.read(readSheet);
源碼調試:
public ExcelReader read(ReadSheet... readSheet) {
return read(Arrays.asList(readSheet));
}
繼續點選read方法
public ExcelReader read(List<ReadSheet> readSheetList) {
excelAnalyser.analysis(readSheetList, Boolean.FALSE);
return this;
}
最主要的是這個類:excelAnalyser
XlsxSaxAnalyser負責解析excel的每個格子的資料。
DefaultAnalysisEventProcessor會擷取解析到的資料,并各個readListener的子類比如我們自定義的demoDataListener的invoke方法,然後由各個listener對資料進行處理。
進入analysis:
ExcelAnalyserImpl
點選進入:
XlsxSaxAnalyser
看下解析每行資料的代碼:
會進入
然後進入parseXmlSource方法:
DefaultAnalysisEventProcessor
dealData():
這個是調用自定義監聽處理每行資料的invoke的入口:
DemoDatalistener的invoke()方法被調用
傳回到XlsxSaxAnalyser
處理完每行之後
進入endSheet()方法:
DefaultAnalysisEventProcessor的endSheet()
for循環裡面是調用自定義監聽的lDemoDataistener的
DemoDataistener的doAfterAllAnalysed()
自此,走完了所有的流程。
備注:官方給的詳細參數介紹
關于常見類解析
- EasyExcel 入口類,用于建構開始各種操作
- ExcelReaderBuilder ExcelWriterBuilder 建構出一個 ReadWorkbook WriteWorkbook,可以了解成一個excel對象,一個excel隻要建構一個
- ExcelReaderSheetBuilder ExcelWriterSheetBuilder 建構出一個 ReadSheet WriteSheet對象,可以了解成excel裡面的一頁,每一頁都要建構一個
- ReadListener 在每一行讀取完畢後都會調用ReadListener來處理資料
- WriteHandler 在每一個操作包括建立單元格、建立表格等都會調用WriteHandler來處理資料
- 所有配置都是繼承的,Workbook的配置會被Sheet繼承,是以在用EasyExcel設定參數的時候,在EasyExcel…sheet()方法之前作用域是整個sheet,之後針對單個sheet
讀
注解
-
指定目前字段對應excel中的那一列。可以根據名字或者Index去比對。當然也可以不寫,預設第一個字段就是index=0,以此類推。千萬注意,要麼全部不寫,要麼全部用index,要麼全部用名字去比對。千萬别三個混着用,除非你非常了解源代碼中三個混着用怎麼去排序的。ExcelProperty
-
預設所有字段都會和excel去比對,加了這個注解會忽略該字段ExcelIgnore
-
日期轉換,用DateTimeFormat
去接收excel日期格式的資料會調用這個注解。裡面的String
參照value
java.text.SimpleDateFormat
-
數字轉換,用NumberFormat
去接收excel數字格式的資料會調用這個注解。裡面的String
參照value
java.text.DecimalFormat
-
預設不加ExcelIgnoreUnannotated
的注解的都會參與讀寫,加了不會參與ExcelProperty
參數
通用參數
ReadWorkbook
,
ReadSheet
都會有的參數,如果為空,預設使用上級。
-
轉換器,預設加載了很多轉換器。也可以自定義。converter
-
監聽器,在讀取資料的過程中會不斷的調用監聽器。readListener
-
需要讀的表格有幾行頭資料。預設有一行頭,也就是認為第二行開始起為資料。headRowNumber
-
與head
二選一。讀取檔案頭對應的清單,會根據清單比對資料,建議使用class。clazz
-
與clazz
二選一。讀取檔案的頭對應的class,也可以使用注解。如果兩個都不指定,則會讀取全部資料。head
-
字元串、表頭等資料自動trimautoTrim
-
讀的時候是否需要使用密碼password
ReadWorkbook(了解成excel對象)參數
-
目前excel的類型 預設會自動判斷excelType
-
與inputStream
二選一。讀取檔案的流,如果接收到的是流就隻用,不用流建議使用file
參數。因為使用了file
easyexcel會幫忙建立臨時檔案,最終還是inputStream
file
-
與file
二選一。讀取檔案的檔案。inputStream
-
自動關閉流。autoCloseStream
-
預設小于5M用 記憶體,超過5M會使用readCache
,這裡不建議使用這個參數。EhCache
ReadSheet(就是excel的一個Sheet)參數
-
需要讀取Sheet的編碼,建議使用這個來指定讀取哪個SheetsheetNo
-
根據名字去比對Sheet,excel 2003不支援根據名字去比對sheetName
寫
注解
-
index 指定寫到第幾列,預設根據成員變量排序。ExcelProperty
指定寫入的名稱,預設成員變量的名字,多個value
可以參照快速開始中的複雜頭value
-
預設所有字段都會寫入excel,這個注解會忽略這個字段ExcelIgnore
-
日期轉換,将DateTimeFormat
寫到excel會調用這個注解。裡面的Date
參照value
java.text.SimpleDateFormat
-
數字轉換,用NumberFormat
寫excel會調用這個注解。裡面的Number
參照value
java.text.DecimalFormat
-
預設不加ExcelIgnoreUnannotated
的注解的都會參與讀寫,加了不會參與ExcelProperty
參數
通用參數
WriteWorkbook
,
WriteSheet
,
WriteTable
都會有的參數,如果為空,預設使用上級。
-
轉換器,預設加載了很多轉換器。也可以自定義。converter
-
寫的處理器。可以實作writeHandler
,WorkbookWriteHandler
,SheetWriteHandler
,RowWriteHandler
,在寫入excel的不同階段會調用CellWriteHandler
-
距離多少行後開始。也就是開頭空幾行relativeHeadRowIndex
-
是否導出頭needHead
-
與head
二選一。寫入檔案的頭清單,建議使用class。clazz
-
與clazz
二選一。寫入檔案的頭對應的class,也可以使用注解。head
-
字元串、表頭等資料自動trimautoTrim
WriteWorkbook(了解成excel對象)參數
-
目前excel的類型 預設excelType
xlsx
-
與outputStream
二選一。寫入檔案的流file
-
與file
二選一。寫入的檔案outputStream
-
模闆的檔案流templateInputStream
-
模闆檔案templateFile
-
自動關閉流。autoCloseStream
-
寫的時候是否需要使用密碼password
-
寫的時候是否是使用預設頭useDefaultStyle
WriteSheet(就是excel的一個Sheet)參數
-
需要寫入的編碼。預設0sheetNo
-
需要些的Sheet名稱,預設同sheetName
sheetNo
WriteTable(就把excel的一個Sheet,一塊區域看一個table)參數
-
需要寫入的編碼。預設0tableNo
10M以上檔案讀取說明
03版沒有辦法處理,相對記憶體占用大很多。excel 07版本有個共享字元串共享字元串的概念,這個會非常占用記憶體,如果全部讀取到記憶體的話,大概是excel檔案的大小的3-10倍,是以easyexcel用存儲檔案的,然後再反序列化去讀取的政策來節約記憶體。當然需要通過檔案反序列化以後,效率會降低,大概降低30-50%(不一定,也看命中率,可能會超過100%)
如果對讀取效率感覺還能接受,就用預設的,永久占用(單個excel讀取整個過程)一般不會超過50M(大機率就30M),剩下臨時的GC會很快回收
預設大檔案處理
預設大檔案處理會自動判斷,共享字元串5M以下會使用記憶體存儲,大概占用15-50M的記憶體,超過5M則使用檔案存儲,然後檔案存儲也要設定多記憶體M用來存放臨時的共享字元串,預設20M。除了共享字元串占用記憶體外,其他占用較少,是以可以預估10M,是以預設大概30M就能讀取一個超級大的檔案。
根據實際需求配置記憶體
想自定義設定,首先要确定你大概願意花多少記憶體來讀取一個超級大的excel,比如希望讀取excel最多占用100M記憶體(是讀取過程中永久占用,新生代馬上回收的不算),那就設定使用檔案來存儲共享字元串的大小判斷為20M(小于20M存記憶體,大于存臨時檔案),然後設定檔案存儲時臨時共享字元串占用記憶體大小90M差不多
如果最大檔案條數也就十幾二十萬,然後excel也就是十幾二十M,而且不會有很高的并發,并且記憶體也較大
// 強制使用記憶體存儲,這樣大概一個20M的excel使用150M(很多臨時對象,是以100M會一直GC)的記憶體
// 這樣效率會比上面的複雜的政策高很多
// 這裡再說明下 就是加了個readCache(new MapCache()) 參數而已,其他的參照其他demo寫 這裡沒有寫全
EasyExcel.read().readCache(new MapCache());
對并發要求較高,而且都是經常有超級大檔案
// 第一個參數的意思是 多少M共享字元串以後 采用檔案存儲 機關MB 預設5M
// 第二個參數 檔案存儲時,記憶體存放多少M緩存資料 預設20M
// 比如 你希望用100M記憶體(這裡說的是解析過程中的永久占用,臨時對象不算)來解析excel,前面算過了 大概是 20M+90M 是以設定參數為:20 和 90
// 這裡再說明下 就是加了個readCacheSelector(new SimpleReadCacheSelector(5, 20))參數而已,其他的參照其他demo寫 這裡沒有寫全
EasyExcel.read().readCacheSelector(new SimpleReadCacheSelector(5, 20));
關于maxCacheActivateSize 也就是前面第二個參數的詳細說明
easyexcel在使用檔案存儲的時候,會把共享字元串拆分成1000條一批,然後放到檔案存儲。然後excel來讀取共享字元串大機率是按照順序的,是以預設20M的1000條的資料放在記憶體,命中後直接傳回,沒命中去讀檔案。是以不能設定太小,太小了,很難命中,一直去讀取檔案,太大了的話會占用過多的記憶體。
如何判斷 maxCacheActivateSize是否需要調整
開啟debug日志會輸出
Already put :4000000
最後一次輸出,大概可以得出值為400W,然後看
Cache misses count:4001
得到值為4K,400W/4K=1000 這代表已經
maxCacheActivateSize
已經非常合理了。如果小于500 問題就非常大了,500到1000 應該都還行。
個人微信公衆号:
搜尋: 怒放de每一天
不定時推送相關文章,期待和大家一起成長!!