Java 中EasyExcel的使用方式
背景
系統(tǒng)中經(jīng)常要導(dǎo)出大量的數(shù)據(jù),格式基本上都是Excel,然而每次導(dǎo)表都是對(duì)系統(tǒng)內(nèi)存的一次挑戰(zhàn)。
在Java領(lǐng)域,生成或解析Excel的框架比較有名的當(dāng)屬Apache的poi和jxl了。但使用它們,會(huì)面臨著嚴(yán)重的內(nèi)存損耗問(wèn)題。如果系統(tǒng)的并發(fā)量還不行,一旦導(dǎo)出大量數(shù)據(jù),便會(huì)出現(xiàn)JVM頻繁full gc,甚至導(dǎo)致OOM。
EasyExcel是阿里巴巴開源的一個(gè)Excel處理框架,使用簡(jiǎn)單、節(jié)省內(nèi)存。節(jié)省內(nèi)存的原理也很簡(jiǎn)單,在解析Excel時(shí)沒(méi)有將文件數(shù)據(jù)全部加載到內(nèi)存當(dāng)中,而是從磁盤文件中一行行讀取。
今天這篇文章就帶大家來(lái)了解一下EasyExcel的使用,個(gè)人使用后的感慨是:太簡(jiǎn)單易用了。
項(xiàng)目構(gòu)建及依賴
首先創(chuàng)建一個(gè)Maven項(xiàng)目,在pom文件中添加如下依賴:
<dependency> ? ? <groupId>com.alibaba</groupId> ? ? <artifactId>easyexcel</artifactId> ? ? <version>2.2.8</version> </dependency>
當(dāng)引入該依賴之后,會(huì)發(fā)現(xiàn)在項(xiàng)目的依賴文件中同時(shí)多出了poi的類庫(kù)。也就是說(shuō),EasyExcel是基于poi來(lái)進(jìn)行實(shí)現(xiàn)的,間接地引入了如下依賴:
<dependency> ? ? <groupId>org.apache.poi</groupId> ? ? <artifactId>poi</artifactId> ? ? <version>3.17</version> </dependency> <dependency> ? ? <groupId>org.apache.poi</groupId> ? ? <artifactId>poi-ooxml</artifactId> ? ? <version>3.17</version> </dependency>
所以,當(dāng)你的項(xiàng)目中已經(jīng)引入了poi的依賴,要考慮一下版本的兼容問(wèn)題。
創(chuàng)建實(shí)體類
EasyExcel易用性的體現(xiàn)之一就是可以通過(guò)在實(shí)體類中使用注解的形式,來(lái)與Excel中的表頭進(jìn)行綁定。
現(xiàn)在直接上實(shí)體類:
@Data
public class UserData {
?
@ExcelProperty(index = 0, value = "姓名")
private String username;
?
@ExcelProperty(index = 1, value = "年齡")
private int age;
?
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(index = 2, value = "生日")
private Date birthday;
}在上面的實(shí)體類中@Data為L(zhǎng)ombok的注解,當(dāng)然你可以自行生成getter/setter方法,其他的注解均為EasyExcel提供的:
- @ExcelProperty:用于設(shè)置Excel表頭,其中index用戶表頭的編號(hào),從0開始;value為表頭對(duì)應(yīng)的內(nèi)容。
- @DateTimeFormat:用于日期的格式化。
完成上述功能準(zhǔn)備工作之后,我們先來(lái)生成一個(gè)Excel。
生成Excel
下面直接展示生成Excel的示例代碼:
public class EasyExcelDemo {
public static void main(String[] args) {
// 實(shí)現(xiàn)excel寫操作
//1.設(shè)置寫入文件夾地址和excel文件名稱
String fileName = "/Users/zzs/temp/excel/write.xlsx";
//調(diào)用easyExcel里面的方法實(shí)現(xiàn)寫操作
//2個(gè)參數(shù),第一個(gè)參數(shù)是文件名稱,第二個(gè)參數(shù)是實(shí)體類
EasyExcel.write(fileName, UserData.class).sheet("學(xué)生信息表").doWrite(getData());
}
?
//創(chuàng)建方法返回list集合
public static List<UserData> getData() {
List<UserData> list = new ArrayList<>();
?
UserData userData1 = new UserData();
userData1.setUsername("張三");
userData1.setAge(22);
userData1.setBirthday(formatDate("2000-10-11"));
list.add(userData1);
?
UserData userData2 = new UserData();
userData2.setUsername("李四");
userData2.setAge(23);
userData2.setBirthday(formatDate("1999-5-3"));
list.add(userData2);
return list;
}
public static Date formatDate(String birthday) {
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");
try {
return sdf.parse(birthday);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}除了準(zhǔn)備數(shù)據(jù)的代碼,核心代碼只有main方法中調(diào)用的EasyExcel.write方法,就是如此的簡(jiǎn)單。EasyExcel的write方法會(huì)根據(jù)傳入的數(shù)據(jù)和實(shí)體類UserData進(jìn)行綁定,生成Excel文件。
我們來(lái)看一下Excel的效果:

生成效果還不錯(cuò),而且使用起來(lái)是不是非常簡(jiǎn)單?
解析Excel
再來(lái)看看解析Excel的操作,直接用上面生成的Excel文件。
首先創(chuàng)建一個(gè)監(jiān)聽器ExcelListener,集成EasyExcel提供AnalysisEventListener類:
public class ExcelListener extends AnalysisEventListener<UserData> {
/**
* 一行一行的讀取excel內(nèi)容
*/
@Override
public void invoke(UserData data, AnalysisContext analysisContext) {
System.out.println("****" + data);
}
/**
* 讀取表頭內(nèi)容
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表頭" + headMap);
}
/**
* 讀取完成操作
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("讀取Excel完畢");
}
}在該監(jiān)聽器中,通過(guò)重寫AnalysisEventListener的方法來(lái)獲得解析的數(shù)據(jù)、表頭信息,以及解析完畢之后執(zhí)行的操作信息。
同樣寫Excel一樣,通過(guò)EasyExcel類的靜態(tài)方法來(lái)執(zhí)行讀操作:
public class EasyExcelReadDemo {
public static void main(String[] args) {
// 實(shí)現(xiàn)excel寫操作
//1.設(shè)置寫入文件夾地址和excel文件名稱
String fileName = "/Users/zzs/temp/excel/write.xlsx";
//調(diào)用easyExcel里面的方法實(shí)現(xiàn)寫操作
//2個(gè)參數(shù),第一個(gè)參數(shù)是文件名稱,第二個(gè)參數(shù)是實(shí)體類
EasyExcel.read(fileName, UserData.class, new ExcelListener()).sheet().doRead();
}
}執(zhí)行上述方法,打印信息如下:
表頭{0=姓名, 1=年齡, 2=生日}
****UserData(username=張三, age=22, birthday=Wed Oct 11 00:00:00 CST 2000)
****UserData(username=李四, age=23, birthday=Mon May 03 00:00:00 CST 1999)
讀取Excel完畢
最先是打印了表頭信息,這里也可以看到表頭的排序是從0開始的。然后,讀取并打印了對(duì)應(yīng)的Excel內(nèi)容,兩條數(shù)據(jù);最后,執(zhí)行讀取完的方法中的日志打印。
看完了上面的整個(gè)操作,解析Excel是不是變得非常簡(jiǎn)單了?再也不為解析Excel犯愁了。
其他相關(guān)特殊用法
上面提到的@DateTimeFormat注解可轉(zhuǎn)換日期格式,還有其他類似功能的注解和自定義轉(zhuǎn)換器。
自定義轉(zhuǎn)換器
通過(guò)自定義轉(zhuǎn)換器,比如將1、0轉(zhuǎn)換成男、女的實(shí)例:
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class SexConverter implements Converter<Integer> {
? @Override
? public Class<Integer> supportJavaTypeKey() {
? ? ? return Integer.class;
? }
? @Override
? public CellDataTypeEnum supportExcelTypeKey() {
? ? ? return CellDataTypeEnum.STRING;
? }
? @Override
? public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
? ? ? return "男".equals(cellData.getStringValue()) ? 1 : 0;
? }
? @Override
? public CellData<String> convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
? ? ? return new CellData<>(integer.equals(1) ? "男" : "女");
? }
}性別屬性注入SexConverter轉(zhuǎn)換器:
@ExcelProperty(value = "性別", converter = SexConverter.class) private Integer sex;
再次生成Excel,性別字段內(nèi)容便顯示為:男、女字樣。
保留兩位小數(shù)
比如體重需要保留兩位小數(shù),可通過(guò)@NumberFormat 注解實(shí)現(xiàn):
@ExcelProperty(value = "體重KG")
@NumberFormat("0.##") // 會(huì)以字符串形式生成單元格,要計(jì)算的列不推薦
private BigDecimal weight;另外一種方法是使用@ContentStyle注解:
@ContentStyle(dataFormat = 2) private BigDecimal weight2;
這樣也能達(dá)到保留兩位小數(shù)的效果。
當(dāng)然,也可以使用實(shí)現(xiàn)Converter接口的方式實(shí)現(xiàn)(同性別實(shí)現(xiàn))。
排除指定Excel列
在很多場(chǎng)景下,Excel的列與實(shí)體類可能并不完全一致,這時(shí)就需要排除一些實(shí)體類的字段。
方式一:類上加注解 @ExcelIgnoreUnannotated,過(guò)濾屬性沒(méi)有@ExcelProperty注解的字段
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor // 一定要有無(wú)參構(gòu)造方法
@ExcelIgnoreUnannotated
public class UserData {
? .....
}方式二:指定字段加@ExcelIgnore注解
@ExcelIgnore // 該字段不生成excel private String remark;
方式三:代碼指定過(guò)濾字段,通過(guò)excludeColumnFiledNames方法:
EasyExcel.write(fileName, UserData.class).sheet("學(xué)生信息表").excludeColumnFiledNames(Arrays.asList("remark")).doWrite(getData());這種方法的好處是:同一Excel可以在調(diào)用方法時(shí)排除不同的數(shù)據(jù)列。
小結(jié)
本文介紹了EasyExcel的使用,整體而言操作簡(jiǎn)單、使用方便,提供了不少注解,方便與實(shí)體對(duì)象之間的關(guān)系綁定。而且官網(wǎng)也提供了相關(guān)的性能數(shù)據(jù),更多的API使用大家還可以繼續(xù)探索。
無(wú)論從性能或易用性上來(lái)說(shuō),都值得你嘗試。特別是臨時(shí)寫一個(gè)Excel的解析或生成的工具,再也不用惆悵一行行的解析了,趕緊收藏用起來(lái)吧。
到此這篇關(guān)于Java 中EasyExcel的使用方式的文章就介紹到這了,更多相關(guān)Java 中EasyExcel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java利用EasyExcel解析動(dòng)態(tài)表頭及導(dǎo)出實(shí)現(xiàn)過(guò)程
- java利用easyexcel實(shí)現(xiàn)導(dǎo)入與導(dǎo)出功能
- Java?easyExcel的復(fù)雜表頭多級(jí)表頭導(dǎo)入
- Java使用EasyExcel進(jìn)行單元格合并的問(wèn)題詳解
- Java利用EasyExcel實(shí)現(xiàn)合并單元格
- Java?easyexcel使用教程之導(dǎo)出篇
- java EasyExcel面向Excel文檔讀寫邏輯示例詳解
- java EasyExcel實(shí)現(xiàn)動(dòng)態(tài)列解析和存表
相關(guān)文章
springboot啟動(dòng)腳本start.sh和停止腳本 stop.sh的詳細(xì)教程
這篇文章主要介紹了springboot啟動(dòng)腳本start.sh和停止腳本 stop.sh的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
SpringMVC通過(guò)攔截器實(shí)現(xiàn)IP黑名單
這篇文章主要為大家詳細(xì)介紹了SpringMVC通過(guò)攔截器實(shí)現(xiàn)IP黑名單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
學(xué)Java做項(xiàng)目需要學(xué)習(xí)的一些技能
這篇文章主要介紹了學(xué)Java做項(xiàng)目需要學(xué)習(xí)的一些技能,例如JavaSE、Servlet、JSP等,總結(jié)了他們中需要學(xué)習(xí)的東西,都是一些經(jīng)驗(yàn)總結(jié),需要的朋友可以參考下2014-07-07
java.lang.NoClassDefFoundError錯(cuò)誤解決辦法
這篇文章主要介紹了java.lang.NoClassDefFoundError錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06
springboot整合security和vue的實(shí)踐
本文主要介紹了springboot整合security和vue的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Java代碼執(zhí)行順序——類的初始化場(chǎng)景
這篇文章主要為大家介紹了Java代碼執(zhí)行順序類的初始化場(chǎng)景實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
Java16新特性record類使用細(xì)節(jié)示例詳解
這篇文章主要為大家介紹了Java16新特性record類使用細(xì)節(jié)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

