介绍
快速开始
引入依赖
简单导出
定义实体类
复杂导出
简单导入
参考资料
介绍
EasyExcel 是一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。它能让你在不用考虑性能、内存的等因素的情况下,快速完成 Excel 的读、写等功能。
EasyExcel文档地址:
https://easyexcel.opensource.alibaba.com/
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
快速开始
引入依赖
com.alibaba easyexcel 3.1.3
简单导出
以导出用户信息为例,接下来手把手教大家如何使用EasyExcel实现导出功能!
定义实体类
在EasyExcel中,以面向对象思想来实现导入导出,无论是导入数据还是导出数据都可以想象成具体某个对象的集合,所以为了实现导出用户信息功能,首先创建一个用户对象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: 用于设置数字转换格式。
自定义转换器
在EasyExcel中,如果想实现枚举类型到字符串类型转换(例如gender属性:1 -> 男,2 -> 女),需实现Converter接口来自定义转换器,下面为自定义GenderConverter性别转换器代码实现:
/** *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"); } /** *读取用户列表数据 * *@return用户列表数据 *@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 注解即可实现,遗憾的是 EasyExcel 不支持一对多导出,只能自行实现,通过此issues了解到,项目维护者建议通过自定义合并策略方式来实现一对多导出。

解决思路:只需把订单主键相同的列中需要合并的列给合并了,就可以实现这种一对多嵌套信息的导出
自定义注解
创建一个自定义注解,用于标记哪些属性需要合并单元格,哪个属性是主键:
/**
*用于判断是否需要合并以及合并的主键
*
*@authorwilliam@StarImmortal
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceExcelMerge{
/**
*是否合并单元格
*
*@returntrue||false
*/
booleanmerge()defaulttrue;
/**
*是否为主键(即该字段相同的行合并)
*
*@returntrue||false
*/
booleanisPrimaryKey()defaultfalse;
}
定义实体类
在需要合并单元格的属性上设置 @ExcelMerge 注解,二级表头通过设置 @ExcelProperty 注解中 value 值为数组形式来实现该效果:
/**
*@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="创建时间")
@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={"商品信息","商品数量"})
@ColumnWidth(20)
privateIntegercount;
}
数据映射与平铺
导出之前,需要对数据进行处理,将订单数据进行平铺,orderList为平铺前格式,exportData为平铺后格式:

自定义单元格合并策略
当 Excel 中两列主键相同时,合并被标记需要合并的列:
/**
*自定义单元格合并策略
*
*@authorwilliam@StarImmortal
*/
publicclassExcelMergeStrategyimplementsRowWriteHandler{
/**
*主键下标
*/
privateIntegerprimaryKeyIndex;
/**
*需要合并的列的下标集合
*/
privatefinalListmergeColumnIndexList=newArrayList<>();
/**
*数据类型
*/
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);
}
//判断是否需要和上一行进行合并
//不能和标题合并,只能数据行之间合并
if(row.getRowNum()<= 1) {
return;
}
// 获取上一行数据
Row lastRow = sheet.getRow(row.getRowNum() - 1);
// 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并
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 < fields.length; i++) {
Cell cell = titleRow.getCell(i);
if (null == cell) {
continue;
}
// 将字段和表头匹配上
if (excelProperty.value()[0].equalsIgnoreCase(cell.getStringCellValue())) {
if (excelMerge.isPrimaryKey()) {
primaryKeyIndex = i;
}
if (excelMerge.merge()) {
mergeColumnIndexList.add(i);
}
}
}
}
// 没有指定主键,则异常
if (null == this.primaryKeyIndex) {
throw new IllegalStateException("使用@ExcelMerge注解必须指定主键");
}
}
}
定义接口
将自定义合并策略 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实现导入功能!
/**
*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();
}
}
}

责任编辑:彭菁
-
内存
+关注
关注
9文章
3173浏览量
76118 -
文件
+关注
关注
1文章
587浏览量
25918 -
Excel
+关注
关注
4文章
230浏览量
57553
原文标题:SpringBoot 集成 EasyExcel 3.x 优雅实现 Excel 导入导出
文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
Linux平台大文件生成和处理方法
ReqMan需求提取和协同处理工具怎么样看了就知道
介绍一款苹果操作系统的电源管理工具
TXT大文件切割软体应用程序免费下载
EXCEL“大文件Vlookup工具”使用步骤资料下载
如何通过python轻松处理大文件
如何解决内存溢出
java内存溢出排查方法
jvm内存溢出故障排查
jvm内存溢出该如何定位解决
Spire.Cloud.Excel云端Excel文档处理SDK

一款解决大文件内存溢出的 Excel 处理工具
评论