茫茫網海中的冷日 - 對這文章發表回應
茫茫網海中的冷日
         
茫茫網海中的冷日
發生過的事,不可能遺忘,只是想不起來而已!
 恭喜您是本站第 1729597 位訪客!  登入  | 註冊
主選單

Google 自訂搜尋

Goole 廣告

隨機相片
IMG_60D_00051.jpg

授權條款

使用者登入
使用者名稱:

密碼:


忘了密碼?

現在就註冊!

對這文章發表回應

發表限制: 非會員 可以發表

發表者: 冷日 發表時間: 2019/7/24 14:29:30

JAVA解析Excel工具 easyexcel

Java解析、生成Excel比較有名的框架有Apache poi、jxl。但他們都存在一個嚴重的問題就是非常的耗內存,poi有一套SAX模式的API可以一定程度的解決一些內存溢出的問題,但POI還是有一些缺陷,比如07版Excel解壓縮以及解壓後存儲都是在內存中完成的,內存消耗依然很大。
easyexcel重寫了poi對07版Excel的解析,能夠原本一個3M的excel用POI sax依然需要100M左右內存降低到KB級別,並且再大的excel不會出現內存溢出,03版依賴POI的sax模式。在上層做了模型轉換的封裝,讓使用者更加簡單方便

環境 Java 1.7 +

maven 3.0.5 +

1. 準備pom.xml



com.alibaba
easyexcel
{latestVersion}


目前最新版本是1.1.1(2018-11-5)
注意: 該版本下使用的POI版本為3.17,所以當項目中的POI版本不為3.17時(有可能項目之前已經引入POI,easyexcel默認自帶版本為3.17),可以考慮升級或者參考文末方法

2. 創建實體

假設Excel中列表為
image

先創建相應的實體User.java


@Data
public class User extends BaseRowModel {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "暱稱", index = 1)
private String nickName;
@ExcelProperty(value = "密碼", index = 2)
private String password;
@ExcelProperty(value = "生日", index = 3, format = "yyyy/MM/dd")
private Date birthday;
}

注意,該實體必須繼承 BaseRowModel

3. 編寫監聽類,該類用於返回讀取到的對象


/**
* @author WuShukai
* @version V1.0
* @description 處理Excel,將讀取到數據保存為對象並輸出
* @date 2018/11/6 16:44
*/
public class ExcelListener extends AnalysisEventListener {
/**
* 自定義用於暫時存儲data。
* 可以通過實例獲取該值
*/
private final List data = new ArrayList<>();
@Override
public void invoke(T object, AnalysisContext context) {
//數據存儲
data.add(object);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
public List getData() {
return data;
}
}

4. 編寫工具類


    /**
* 從Excel中讀取文件,讀取的文件是一個DTO類,該類必須繼承BaseRowModel
* 具體實例參考 : MemberMarketDto.java
* 參考:https://github.com/alibaba/easyexcel
* 字符流必須支持標記,FileInputStream 不支持標記,可以使用BufferedInputStream 代替
* BufferedInputStream bis = new BufferedInputStream(new FileInputStream(...));
*
* @param inputStream 文件輸入流
* @param clazz 繼承該類必須繼承BaseRowModel的類
* @return 讀取完成的list
*/
public static List readExcel(final InputStream inputStream, final Class clazz) {
if (null == inputStream) {
throw new NullPointerException("the inputStream is null!");
}
AnalysisEventListener listener = new ExcelListener();
//讀取xls 和 xlxs格式
//如果POI版本為3.17,可以如下聲明
ExcelReader reader = new ExcelReader(inputStream, null, listener);
//判斷格式,針對POI版本低於3.17
//ExcelTypeEnum excelTypeEnum = valueOf(inputStream);
//ExcelReader reader = new ExcelReader(inputStream, excelTypeEnum, null, listener);
reader.read(new com.alibaba.excel.metadata.Sheet(1, 1, clazz));
return ((ExcelListener) listener).getData();
}
/**
* 需要寫入的Excel,有模型映射關係
*
* @param file 需要寫入的Excel,格式為xlsx
* @param list 寫入Excel中的所有數據,繼承於BaseRowModel
*/
public static void writeExcel(final File file, List list) {
OutputStream out = new FileOutputStream(file);
try {
ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
//寫第一個sheet, 有模型映射關係
Class t = list.get(0).getClass();
com.alibaba.excel.metadata.Sheet sheet = new com.alibaba.excel.metadata.Sheet(1, 0, t);
writer.write(list, sheet);
writer.finish();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 根據輸入流,判斷為xls還是xlsx,該方法原本存在於easyexcel 1.1.0 的ExcelTypeEnum中。
* 如果POI版本為3.17以下,則FileMagic會報錯,找不到該類,此時去到POI 3.17中將FileMagic抽取出來
*/
public static ExcelTypeEnum valueOf(InputStream inputStream) {
try {
FileMagic fileMagic = FileMagic.valueOf(inputStream);
if (FileMagic.OLE2.equals(fileMagic)) {
return ExcelTypeEnum.XLS;
}
if (FileMagic.OOXML.equals(fileMagic)) {
return ExcelTypeEnum.XLSX;
}
throw new IllegalArgumentException("excelTypeEnum can not null");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

5. POI版本過低處理

注意:當POI版本低於easyexcel中內置的POI版本不一致的時候,只能使用被聲明為過期的方法


ExcelReader reader = new ExcelReader(inputStream, ExcelTypeEnum.XLSX, null, new AnalysisEventListener>() {...});

無法自動判斷Excel為03還是07版本,此時可以將缺少POI 3.17 中的方法拷貝出來使用。



/**
* @author WuShukai
* @version V1.0
* @description 判斷格式,這個枚舉存在於poi 3.17,但是目前版本是3.15,所以從3.17抽出來使用
* @date 2018/11/6 16:46
*/
public enum FileMagic {
/**
* OLE2 / BIFF8+ stream used for Office 97 and higher documents
*/
OLE2(HeaderBlockConstants._signature),
/**
* OOXML / ZIP stream
*/
OOXML(org.apache.poi.poifs.common.POIFSConstants.OOXML_FILE_HEADER),
/**
* UNKNOWN magic
*/
UNKNOWN(new byte[0]);
final byte[][] magic;
FileMagic(long magic) {
this.magic = new byte[1][8];
LittleEndian.putLong(this.magic[0], 0, magic);
}
FileMagic(byte[]... magic) {
this.magic = magic;
}
public static FileMagic valueOf(byte[] magic) {
for (FileMagic fm : values()) {
int i = 0;
boolean found = true;
for (byte[] ma : fm.magic) {
for (byte m : ma) {
byte d = magic[i++];
if (!(d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {
found = false;
break;
}
}
if (found) {
return fm;
}
}
}
return UNKNOWN;
}
/**
* @param inp An InputStream which supports either mark/reset
*/
public static FileMagic valueOf(InputStream inp) throws IOException {
if (!inp.markSupported()) {
throw new IOException("getFileMagic() only operates on streams which support mark(int)");
}
// Grab the first 8 bytes
byte[] data = IOUtils.peekFirst8Bytes(inp);
return FileMagic.valueOf(data);
}
}

6. InputStream無法標記錯誤,error for mark(in);

因為FileInputStream是無法被標記的,可以將FileInputStream替換成BufferedInputStream。


try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
do something...
}


原文出處: 使用easyexcel读写Excel - Sky`s Blog
內容圖示
url email imgsrc image code quote
樣本
bold italic underline linethrough   












 [詳情...]
validation picture

注意事項:
預覽不需輸入認證碼,僅真正發送文章時才會檢查驗證碼。
認證碼有效期10分鐘,若輸入資料超過10分鐘,請您備份內容後,重新整理本頁並貼回您的內容,再輸入驗證碼送出。

選項

Powered by XOOPS 2.0 © 2001-2008 The XOOPS Project|