介紹
快速開始
引入依賴
簡單導出
定義實體類
復雜導出
簡單導入
參考資料
介紹
EasyExcel 是一個基于 Java 的、快速、簡潔、解決大文件內存溢出的 Excel 處理工具。它能讓你在不用考慮性能、內存的等因素的情況下,快速完成 Excel 的讀、寫等功能。
EasyExcel文檔地址:
https://easyexcel.opensource.alibaba.com/
基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權限、多租戶、數(shù)據(jù)權限、工作流、三方登錄、支付、短信、商城等功能
項目地址:https://github.com/YunaiV/ruoyi-vue-pro
視頻教程:https://doc.iocoder.cn/video/
快速開始
引入依賴
com.alibaba easyexcel 3.1.3
簡單導出
以導出用戶信息為例,接下來手把手教大家如何使用EasyExcel實現(xiàn)導出功能!
定義實體類
在EasyExcel中,以面向對象思想來實現(xiàn)導入導出,無論是導入數(shù)據(jù)還是導出數(shù)據(jù)都可以想象成具體某個對象的集合,所以為了實現(xiàn)導出用戶信息功能,首先創(chuàng)建一個用戶對象UserDO實體類,用于封裝用戶信息:
/**
*用戶信息
*
*@authorwilliam@StarImmortal
*/
@Data
publicclassUserDO{
@ExcelProperty("用戶編號")
@ColumnWidth(20)
privateLongid;
@ExcelProperty("用戶名")
@ColumnWidth(20)
privateStringusername;
@ExcelIgnore
privateStringpassword;
@ExcelProperty("昵稱")
@ColumnWidth(20)
privateStringnickname;
@ExcelProperty("生日")
@ColumnWidth(20)
@DateTimeFormat("yyyy-MM-dd")
privateDatebirthday;
@ExcelProperty("手機號")
@ColumnWidth(20)
privateStringphone;
@ExcelProperty("身高(米)")
@NumberFormat("#.##")
@ColumnWidth(20)
privateDoubleheight;
@ExcelProperty(value="性別",converter=GenderConverter.class)
@ColumnWidth(10)
privateIntegergender;
}
上面代碼中類屬性上使用了EasyExcel核心注解:
@ExcelProperty: 核心注解,value屬性可用來設置表頭名稱,converter屬性可以用來設置類型轉換器;
@ColumnWidth: 用于設置表格列的寬度;
@DateTimeFormat: 用于設置日期轉換格式;
@NumberFormat: 用于設置數(shù)字轉換格式。
自定義轉換器
在EasyExcel中,如果想實現(xiàn)枚舉類型到字符串類型轉換(例如gender屬性:1 -> 男,2 -> 女),需實現(xiàn)Converter接口來自定義轉換器,下面為自定義GenderConverter性別轉換器代碼實現(xiàn):
/** *Excel性別轉換器 * *@authorwilliam@StarImmortal */ publicclassGenderConverterimplementsConverter{ @Override publicClass>supportJavaTypeKey(){ returnInteger.class; } @Override publicCellDataTypeEnumsupportExcelTypeKey(){ returnCellDataTypeEnum.STRING; } @Override publicIntegerconvertToJavaData(ReadConverterContext>context){ returnGenderEnum.convert(context.getReadCellData().getStringValue()).getValue(); } @Override publicWriteCellData>convertToExcelData(WriteConverterContext context){ returnnewWriteCellData<>(GenderEnum.convert(context.getValue()).getDescription()); } } /** *性別枚舉 * *@authorwilliam@StarImmortal */ @Getter @AllArgsConstructor publicenumGenderEnum{ /** *未知 */ UNKNOWN(0,"未知"), /** *男性 */ MALE(1,"男性"), /** *女性 */ FEMALE(2,"女性"); privatefinalIntegervalue; @JsonFormat privatefinalStringdescription; publicstaticGenderEnumconvert(Integervalue){ returnStream.of(values()) .filter(bean->bean.value.equals(value)) .findAny() .orElse(UNKNOWN); } publicstaticGenderEnumconvert(Stringdescription){ returnStream.of(values()) .filter(bean->bean.description.equals(description)) .findAny() .orElse(UNKNOWN); } }
定義接口
/** *EasyExcel導入導出 * *@authorwilliam@StarImmortal */ @RestController @RequestMapping("/excel") publicclassExcelController{ @GetMapping("/export/user") publicvoidexportUserExcel(HttpServletResponseresponse){ try{ this.setExcelResponseProp(response,"用戶列表"); ListuserList=this.getUserList(); EasyExcel.write(response.getOutputStream()) .head(UserDO.class) .excelType(ExcelTypeEnum.XLSX) .sheet("用戶列表") .doWrite(userList); }catch(IOExceptione){ thrownewRuntimeException(e); } } /** *設置響應結果 * *@paramresponse響應結果對象 *@paramrawFileName文件名 *@throwsUnsupportedEncodingException不支持編碼異常 */ privatevoidsetExcelResponseProp(HttpServletResponseresponse,StringrawFileName)throwsUnsupportedEncodingException{ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); StringfileName=URLEncoder.encode(rawFileName,"UTF-8").replaceAll("+","%20"); response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx"); } /** *讀取用戶列表數(shù)據(jù) * *@return用戶列表數(shù)據(jù) *@throwsIOExceptionIO異常 */ privateList getUserList()throwsIOException{ ObjectMapperobjectMapper=newObjectMapper(); ClassPathResourceclassPathResource=newClassPathResource("mock/users.json"); InputStreaminputStream=classPathResource.getInputStream(); returnobjectMapper.readValue(inputStream,newTypeReference >(){ }); } }
測試接口
運行項目,通過 Postman 或者 Apifox 工具來進行接口測試
注意:在 Apifox 中訪問接口后無法直接下載,需要點擊返回結果中的下載圖標才行,點擊之后方可對Excel文件進行保存。
接口地址:http://localhost:8080/excel/export/user


復雜導出
由于 EasyPoi 支持嵌套對象導出,直接使用內置 @ExcelCollection 注解即可實現(xiàn),遺憾的是 EasyExcel 不支持一對多導出,只能自行實現(xiàn),通過此issues了解到,項目維護者建議通過自定義合并策略方式來實現(xiàn)一對多導出。

解決思路:只需把訂單主鍵相同的列中需要合并的列給合并了,就可以實現(xiàn)這種一對多嵌套信息的導出
自定義注解
創(chuàng)建一個自定義注解,用于標記哪些屬性需要合并單元格,哪個屬性是主鍵:
/**
*用于判斷是否需要合并以及合并的主鍵
*
*@authorwilliam@StarImmortal
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceExcelMerge{
/**
*是否合并單元格
*
*@returntrue||false
*/
booleanmerge()defaulttrue;
/**
*是否為主鍵(即該字段相同的行合并)
*
*@returntrue||false
*/
booleanisPrimaryKey()defaultfalse;
}
定義實體類
在需要合并單元格的屬性上設置 @ExcelMerge 注解,二級表頭通過設置 @ExcelProperty 注解中 value 值為數(shù)組形式來實現(xiàn)該效果:
/**
*@authorwilliam@StarImmortal
*/
@Data
publicclassOrderBO{
@ExcelProperty(value="訂單主鍵")
@ColumnWidth(16)
@ExcelMerge(merge=true,isPrimaryKey=true)
privateStringid;
@ExcelProperty(value="訂單編號")
@ColumnWidth(20)
@ExcelMerge(merge=true)
privateStringorderId;
@ExcelProperty(value="收貨地址")
@ExcelMerge(merge=true)
@ColumnWidth(20)
privateStringaddress;
@ExcelProperty(value="創(chuàng)建時間")
@ColumnWidth(20)
@DateTimeFormat("yyyy-MM-ddHHss")
@ExcelMerge(merge=true)
privateDatecreateTime;
@ExcelProperty(value={"商品信息","商品編號"})
@ColumnWidth(20)
privateStringproductId;
@ExcelProperty(value={"商品信息","商品名稱"})
@ColumnWidth(20)
privateStringname;
@ExcelProperty(value={"商品信息","商品標題"})
@ColumnWidth(30)
privateStringsubtitle;
@ExcelProperty(value={"商品信息","品牌名稱"})
@ColumnWidth(20)
privateStringbrandName;
@ExcelProperty(value={"商品信息","商品價格"})
@ColumnWidth(20)
privateBigDecimalprice;
@ExcelProperty(value={"商品信息","商品數(shù)量"})
@ColumnWidth(20)
privateIntegercount;
}
數(shù)據(jù)映射與平鋪
導出之前,需要對數(shù)據(jù)進行處理,將訂單數(shù)據(jù)進行平鋪,orderList為平鋪前格式,exportData為平鋪后格式:

自定義單元格合并策略
當 Excel 中兩列主鍵相同時,合并被標記需要合并的列:
/**
*自定義單元格合并策略
*
*@authorwilliam@StarImmortal
*/
publicclassExcelMergeStrategyimplementsRowWriteHandler{
/**
*主鍵下標
*/
privateIntegerprimaryKeyIndex;
/**
*需要合并的列的下標集合
*/
privatefinalListmergeColumnIndexList=newArrayList<>();
/**
*數(shù)據(jù)類型
*/
privatefinalClass>elementType;
publicExcelMergeStrategy(Class>elementType){
this.elementType=elementType;
}
@Override
publicvoidafterRowDispose(WriteSheetHolderwriteSheetHolder,WriteTableHolderwriteTableHolder,Rowrow,IntegerrelativeRowIndex,BooleanisHead){
//判斷是否為標題
if(isHead){
return;
}
//獲取當前工作表
Sheetsheet=writeSheetHolder.getSheet();
//初始化主鍵下標和需要合并字段的下標
if(primaryKeyIndex==null){
this.initPrimaryIndexAndMergeIndex(writeSheetHolder);
}
//判斷是否需要和上一行進行合并
//不能和標題合并,只能數(shù)據(jù)行之間合并
if(row.getRowNum()<=?1)?{
????????????return;
????????}
????????//?獲取上一行數(shù)據(jù)
????????Row?lastRow?=?sheet.getRow(row.getRowNum()?-?1);
????????//?將本行和上一行是同一類型的數(shù)據(jù)(通過主鍵字段進行判斷),則需要合并
????????if?(lastRow.getCell(primaryKeyIndex).getStringCellValue().equalsIgnoreCase(row.getCell(primaryKeyIndex).getStringCellValue()))?{
????????????for?(Integer?mergeIndex?:?mergeColumnIndexList)?{
????????????????CellRangeAddress?cellRangeAddress?=?new?CellRangeAddress(row.getRowNum()?-?1,?row.getRowNum(),?mergeIndex,?mergeIndex);
????????????????sheet.addMergedRegionUnsafe(cellRangeAddress);
????????????}
????????}
????}
????/**
?????*?初始化主鍵下標和需要合并字段的下標
?????*
?????*?@param?writeSheetHolder?WriteSheetHolder
?????*/
????private?void?initPrimaryIndexAndMergeIndex(WriteSheetHolder?writeSheetHolder)?{
????????//?獲取當前工作表
????????Sheet?sheet?=?writeSheetHolder.getSheet();
????????//?獲取標題行
????????Row?titleRow?=?sheet.getRow(0);
????????//?獲取所有屬性字段
????????Field[]?fields?=?this.elementType.getDeclaredFields();
????????//?遍歷所有字段
????????for?(Field?field?:?fields)?{
????????????//?獲取@ExcelProperty注解,用于獲取該字段對應列的下標
????????????ExcelProperty?excelProperty?=?field.getAnnotation(ExcelProperty.class);
????????????//?判斷是否為空
????????????if?(null?==?excelProperty)?{
????????????????continue;
????????????}
????????????//?獲取自定義注解,用于合并單元格
????????????ExcelMerge?excelMerge?=?field.getAnnotation(ExcelMerge.class);
????????????//?判斷是否需要合并
????????????if?(null?==?excelMerge)?{
????????????????continue;
????????????}
????????????for?(int?i?=?0;?i?
定義接口
將自定義合并策略 ExcelMergeStrategy 通過 registerWriteHandler 注冊上去:
/**
*EasyExcel導入導出
*
*@authorwilliam@StarImmortal
*/
@RestController
@RequestMapping("/excel")
publicclassExcelController{
@GetMapping("/export/order")
publicvoidexportOrderExcel(HttpServletResponseresponse){
try{
this.setExcelResponseProp(response,"訂單列表");
ListorderList=this.getOrderList();
ListexportData=this.convert(orderList);
EasyExcel.write(response.getOutputStream())
.head(OrderBO.class)
.registerWriteHandler(newExcelMergeStrategy(OrderBO.class))
.excelType(ExcelTypeEnum.XLSX)
.sheet("訂單列表")
.doWrite(exportData);
}catch(IOExceptione){
thrownewRuntimeException(e);
}
}
/**
*設置響應結果
*
*@paramresponse響應結果對象
*@paramrawFileName文件名
*@throwsUnsupportedEncodingException不支持編碼異常
*/
privatevoidsetExcelResponseProp(HttpServletResponseresponse,StringrawFileName)throwsUnsupportedEncodingException{
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
StringfileName=URLEncoder.encode(rawFileName,"UTF-8").replaceAll("+","%20");
response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");
}
}
測試接口
運行項目,通過 Postman 或者 Apifox 工具來進行接口測試
注意:在 Apifox 中訪問接口后無法直接下載,需要點擊返回結果中的下載圖標才行,點擊之后方可對Excel文件進行保存。
接口地址:http://localhost:8080/excel/export/order


簡單導入
以導入用戶信息為例,接下來手把手教大家如何使用EasyExcel實現(xiàn)導入功能!
/**
*EasyExcel導入導出
*
*@authorwilliam@StarImmortal
*/
@RestController
@RequestMapping("/excel")
@Api(tags="EasyExcel")
publicclassExcelController{
@PostMapping("/import/user")
publicResponseVOimportUserExcel(@RequestPart(value="file")MultipartFilefile){
try{
ListuserList=EasyExcel.read(file.getInputStream())
.head(UserDO.class)
.sheet()
.doReadSync();
returnResponseVO.success(userList);
}catch(IOExceptione){
returnResponseVO.error();
}
}
}

責任編輯:彭菁
-
內存
+關注
關注
8文章
3159瀏覽量
75976 -
文件
+關注
關注
1文章
586瀏覽量
25874 -
Excel
+關注
關注
4文章
230瀏覽量
57461
原文標題:SpringBoot 集成 EasyExcel 3.x 優(yōu)雅實現(xiàn) Excel 導入導出
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
Linux平臺大文件生成和處理方法
Python利用pandas讀寫Excel文件
ReqMan需求提取和協(xié)同處理工具怎么樣看了就知道
介紹一款蘋果操作系統(tǒng)的電源管理工具
TXT大文件切割軟體應用程序免費下載
EXCEL“大文件Vlookup工具”使用步驟資料下載
如何通過python輕松處理大文件
如何解決內存溢出
java內存溢出排查方法
jvm內存溢出故障排查
jvm內存溢出該如何定位解決
內存溢出與內存泄漏:定義、區(qū)別與解決方案
Spire.Cloud.Excel云端Excel文檔處理SDK

一款解決大文件內存溢出的 Excel 處理工具
評論