文章目录
- Spring Boot集成EasyExcel实现excel导入导出操作
- 0 简要说明
- 简单使用
- 读操作
- excel源文件
- 实体类
- 监听器
- 业务代码
- 写操作
- *实体类*
- excel示例
- 业务代码
- 根据参数指定列导出
- 指定哪几列导出
- 复杂头导出
- 关于数值型,日期型,浮点型数据解决方案
- 实体类接收字符串获取
Easy Excel 官网
Spring Boot集成EasyExcel实现excel导入导出操作
0 简要说明
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。
简单使用
引入依赖
<!--easyexcel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency>
读操作
excel源文件
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TempData {private Long id;/*** 用户名*/private String userName;/*** 生日*/private Date birthday;/*** 性别*/private String sex;/*** 地址*/private String address;}
监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
package com.geekmice.springbootselfexercise;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.geekmice.springbootselfexercise.dao.UserDao;
import com.geekmice.springbootselfexercise.domain.TempData;
import com.geekmice.springbootselfexercise.service.UserService;
import lombok.extern.slf4j.Slf4j;import java.util.List;/*** @BelongsProject: spring-boot-self-exercise* @BelongsPackage: com.geekmice.springbootselfexercise* @Author: pingmingbo* @CreateTime: 2023-08-07 10:19* @Description: easyexcel读操作监听器* @Version: 1.0*/
@Slf4j
public class UserListener implements ReadListener<TempData> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private List<TempData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private UserService userService;/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public UserListener(UserService userService) {this.userService = userService;}/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(TempData data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", cachedDataList.size());// this.s(cachedDataList);// userService.saveBatch(cachedDataList);log.info("存储数据库成功!");}
}
业务代码
//userService业务service,也可以是dao,如果是多个mapper或者service,通过构造方法或者set方法传递
也可以使用工具类getBean获取,不需要通过构造方法,这种方式主要是多个mapper或者service进行业务操作
@Overridepublic void uploadFileByEasyExcel(MultipartFile file) {try {EasyExcel.read(file.getInputStream(), TempData.class, new UserListener(userService)).sheet().doRead();} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}}
写操作
实体类
名称 | 默认值 | 备注 |
---|---|---|
value | 空 | 用于匹配excel中的头,必须全匹配,如果有多行头,会匹配最后一行头 |
order | Integer.MAX_VALUE | 优先级高于value ,会根据order 的顺序来匹配实体和excel中数据的顺序 |
index | -1 | 优先级高于value 和order ,会根据index 直接指定到excel中具体的哪一列 |
converter | 自动选择 | 指定当前字段用什么转换器,默认会自动选择。读的情况下只要实现com.alibaba.excel.converters.Converter#convertToJavaData(com.alibaba.excel.converters.ReadConverterContext<?>) 方法即可 |
ExcelIgnore
默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
ExcelIgnore
默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
package com.geekmice.springbootselfexercise.domain;import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;/*** @BelongsProject: spring-boot-self-exercise* @BelongsPackage: com.geekmice.springbootselfexercise.domain* @Author: pingmingbo* @CreateTime: 2023-08-07 09:53* @Description: TODO* @Version: 1.0*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TempData {/*** 忽略这个字段*/@ExcelIgnoreprivate Long id;/*** 用户名*/@ExcelProperty(value = "用户名")private String userName;/*** 生日*/@ExcelProperty(value ="生日",format = "yyyy-MM-dd")private Date birthday;/*** 性别*/@ExcelProperty(value ="性别")private String sex;/*** 地址*/@ExcelProperty(value = "地址")private String address;}
excel示例
业务代码
@Overridepublic void downloadFileByEasyExcel() {EasyExcel.write("D:\\easyexcel.xls", TempData.class).sheet().doWrite(data());}
根据参数指定列导出
/*** 根据参数指定列导出*/@Testpublic void t2(){List<String> list = new ArrayList(16);list.add("sex");list.add("userName");EasyExcel.write("D://easyexcel_by_columns.xlsx", TempData.class).excludeColumnFieldNames(null).sheet().includeColumnFieldNames(list).doWrite(data());log.info("导出结束 [{}]", DateFormatUtils.format(new Date(), DateUtils.DATE_FORMAT_19));}
指定哪几列导出
使用index属性,index=2空余出来,这样一来第二列为空
复杂头导出
/*** 用户名*/@ExcelProperty(value = {"父级","用户名"},index = 0)private String userName;/*** 生日*/@ExcelProperty(value ={"父级","生日"},format = "yyyy-MM-dd",index = 1)private Date birthday;/*** 性别*/@ExcelProperty(value ={"父级","性别"},index = 2)private String sex;/**List<String> list = new ArrayList(16);list.add("sex");list.add("userName");list.add("birthday");EasyExcel.write("D://easyexcel_by_columns.xlsx", TempData.class).excludeColumnFieldNames(null).sheet().includeColumnFieldNames(list).doWrite(data());log.info("导出结束 [{}]", DateFormatUtils.format(new Date(), DateUtils.DATE_FORMAT_19));
关于数值型,日期型,浮点型数据解决方案
实体类接收字符串获取
package com.geekmice.springbootselfexercise.domain;import cn.afterturn.easypoi.excel.annotation.Excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.geekmice.springbootselfexercise.utils.CustomStringStringConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;/*** @BelongsProject: spring-boot-self-exercise* @BelongsPackage: com.geekmice.springbootselfexercise.domain* @Author: pingmingbo* @CreateTime: 2023-08-07 14:04* @Description: TODO* @Version: 1.0*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ConverterData {/*** 生日*/@ExcelProperty(value = "生日")private String birthday;/*** 性别*/@ExcelProperty(value = "性别")private String sex;/*** 分数*/@ExcelProperty(value = "分数")private String score;
}