easypoi Excel的导入导出(完整示例)
码字不易,还请各位大爷们点赞收藏加关注喔~😘💕
文章目录
前言
easypoi官方文档
一、准备工作,导包
二、导入
2.1 无校验结果导入
2.2 有校验结果的导入
2.3 大数据导入
三、导出
3.1 简单导出
3.2 模板导出
3.3 多 sheet 的模板导出
3.4 大数据导出
方法一:先查出数据,再导
方法二:在导入的方法中查数据
前言
easypoi官方文档
旧地址:EasyPoi教程_V1.0
新地址:1. 前言 - Powered by MinDoc
一、准备工作,导包
easypoi 3.x 的版本和 4.x 的版本有的地方有略微的不同,但最后实现的效果都差不多,这里我用的是 4.4.0 的
cn.afterturn
easypoi-base
4.4.0
cn.afterturn
easypoi-web
4.4.0
cn.afterturn
easypoi-annotation
4.4.0
xerces
xercesImpl
2.12.2
easypoi-annotation :基础注解包,作用与实体对象上,拆分后方便maven多工程的依赖管理
easypoi-base :导入导出的工具包,可以完成Excel导出,导入,Word的导出,Excel的导出功能
easypoi-web :耦合了spring-mvc 基于AbstractView,极大的简化spring-mvc下的导出功能
xercesImpl : 支持通过 Sax 方式的大数据量导入
二、导入
2.1 无校验结果导入
关键代码: ExcelImportUtil.importExcel(InputStream inputstream, Class pojoClass,
ImportParams params);
inputstream: 数据源输入流,也就是文件输入流
pojoClass: 接收文件类型的类
params: 导入参数,直接 new ImportParams()
1. 准备一张数据表,如:
2.定义对应的对象,顺序可以和表中不一样,属性也可以没有,但是需要赋值的属性它的 @Excel(name="") 要和表中字段一一对应上:
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import web_test.model.onecodeend.ExcelVerifyInfo;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
public class ImportEntity {
@Excel(name = "序号")
private String serialNum;
@Excel(name = "姓名")
private String name;
@Excel(name = "性别", replace = {"男_true", "女_false"})
private Boolean sex;
@Excel(name = "电话")
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
private Date buyTime;
@Excel(name = "住址")
private String address;
@Excel(name = "水果", groupName = "订单类别")
private String fruitsName;
@Excel(name = "蔬菜")
private String vegetableName;
}
replace: 会将 "_" 前的内容替换成后面的
format : 会解析Excel 中的时间内容,但必须和 指定解析的格式一样,结合 @JsonFormat 注解,就可以将时间解析为标准格式了
groupName:会自动识别出Excel 水平合并的单元格,直到出现下一个 groupName 为止
controller
@PostMapping("/importExcel")
public List importExcel(@RequestPart("file") MultipartFile file){
return apiTestService.importExcel(file);
}
serviceImpl:
@Override
public List importExcel(MultipartFile file) {
try {
ImportParams params = new ImportParams();
params.setHeadRows(2);
List list = ExcelImportUtil.importExcel(file.getInputStream(), ImportEntity.class, params);
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
结果:
2.2 有校验结果的导入
关键代码: ExcelImportUtil.importExcelMore(InputStream inputstream, Class pojoClass,
ImportParams params);
inputstream: 数据源输入流,也就是文件输入流
pojoClass: 接收文件内容的类的字节码类型
params: 导入参数,直接 new ImportParams()
注意导入的方法是 importExcelMore ,比 无校验的导入 多了一个 More
1. 还是这张表,这里我们把导入的实体类 ImportEntity 改造一下,使其可以额外记录一些错误的信息
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
public class ImportEntity implements IExcelModel, IExcelDataModel {
@Excel(name = "序号")
private String serialNum;
@Excel(name = "姓名")
private String name;
@Excel(name = "性别", replace = {"男_true", "女_false"})
private Boolean sex;
@Excel(name = "电话")
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
private Date buyTime;
@Excel(name = "住址")
private String address;
@Excel(name = "水果", groupName = "订单类别")
private String fruitsName;
@Excel(name = "蔬菜")
private String vegetableName;
/**
* 记录当前是第几行
*/
private Integer rowNum;
/**
* 记录当前行在校验时存在的错误信息
*/
private String errorMsg;
@Override
public Integer getRowNum() {
return this.rowNum;
}
@Override
public void setRowNum(Integer integer) {
this.rowNum = integer;
}
@Override
public String getErrorMsg() {
return this.errorMsg;
}
@Override
public void setErrorMsg(String s) {
this.errorMsg = s;
}
}
controller:
@PostMapping("/importExcel")
public void importExcel(@RequestPart("file") MultipartFile file){
apiTestService.importExcel(file);
}
serviceImpl:
先注入 校验处理器:
@Service
public class ApiTestServiceImpl implements IApiTestService {
@Resource
ExcelVerifyHandler handler;
@Override
public void importExcel(MultipartFile file) {
try {
ImportParams params = new ImportParams();
params.setHeadRows(2);
// 设置是否需要校验为 true,开启校验
params.setNeedVerify(true);
// 设置校验处理器,我们将校验的内容放在这个里面
params.setVerifyHandler(new ExcelVerifyHandler());
// 这里的导入结果里面既有检验失败的数据,也有校验成功的数据
ExcelImportResult importResult = ExcelImportUtil.importExcelMore(file.getInputStream(), ImportEntity.class, params);
// 获取校验成功的数据
List list = importResult.getList();
// 我们来打印信息校验成功的数据看看
list.forEach(System.out::println);
// 获取校验失败的数据
List failList = importResult.getFailList();
// 我们来打印信息校验失败的数据看看
if (failList != null){
failList.forEach(System.out::println);
// 再来看看检验失败数据的错误信息
String errorMsg = failList.stream().map(ImportEntity::getErrorMsg).collect(Collectors.joining(","));
System.out.println("错误原因:" + errorMsg);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ExcelVerifyHandler :
package web_test.handler;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler;
import web_test.model.ImportEntity;
/**
* @author shenlongdaxia
*/
public class ExcelVerifyHandler implements IExcelVerifyHandler {
/**
* 具体校验的方法,它会每一行每一行的校验
*
* @param entity
* @return ExcelVerifyHandlerResult
*/
@Override
public ExcelVerifyHandlerResult verifyHandler(ImportEntity entity) {
System.out.println("正在校验第 " + entity.getRowNum() + " 行");
if (entity.getAddress() == null) {
return new ExcelVerifyHandlerResult(false, "第 " + entity.getRowNum() + " 行地址不能为空");
}
return new ExcelVerifyHandlerResult(true);
}
}
把 Excel 加点错误数据:
结果:
这样直接把 行号、错误信息和实体类 写在一起的话,每条数据都会带着这俩多余的信息,所以我们可以把这两个提出去,让实体类 去继承,最后我们得到的数据就没有多余的内容了:
ImportEntity:
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import web_test.model.onecodeend.ExcelVerifyInfo;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
public class ImportEntity extends ExcelVerifyInfo {
@Excel(name = "序号")
private String serialNum;
@Excel(name = "姓名")
private String name;
@Excel(name = "性别", replace = {"男_true", "女_false"})
private Boolean sex;
@Excel(name = "电话")
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
private Date buyTime;
@Excel(name = "住址")
private String address;
@Excel(name = "水果", groupName = "订单类别")
private String fruitsName;
@Excel(name = "蔬菜")
private String vegetableName;
}
ExcelVerifyInfo :
package web_test.model;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import lombok.Data;
/**
* Excel校验基类
*
* @author shenlongdaxia
*/
@Data
public class ExcelVerifyInfo implements IExcelModel, IExcelDataModel {
private Integer rowNum;
private String errorMsg;
@Override
public String getErrorMsg() {
return errorMsg;
}
@Override
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
@Override
public Integer getRowNum() {
return this.rowNum;
}
@Override
public void setRowNum(Integer rowNum) {
this.rowNum = rowNum;
}
}
结果:
雅~ 😎
2.3 大数据导入
关键代码:void ExcelImportUtil.importExcelBySax(InputStream inputstream, Class pojoClass, ImportParams params, IReadHandler handler);
注意!! 通过 Sax 方式导入是没有返回值的,数据的保存在 IReadHandler 参数里面进行
inputstream: 传入文件的输入流
pojoClass:接收文件内容的实体类的字节码类型
params: 导入参数
handler: 接口自定义处理类,里面有俩个方法,一个是 handler(),这里面就是每条数据要做什么事情,另一个是 doAfterAll(),这里面就是所有数据处理完之后要做什么事情。直接开 new
表: 塞了 100 条数据
实体类:
因为所有的导入数据的处理都在 IReadHandler 中进行,所有我们的实体类就可以不用实现校验了
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import web_test.model.onecodeend.ExcelVerifyInfo;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
public class ImportEntity {
@Excel(name = "序号")
private String serialNum;
@Excel(name = "姓名")
private String name;
@Excel(name = "性别", replace = {"男_true", "女_false"})
private Boolean sex;
@Excel(name = "电话")
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
private Date buyTime;
@Excel(name = "住址")
private String address;
@Excel(name = "水果", groupName = "订单类别")
private String fruitsName;
@Excel(name = "蔬菜")
private String vegetableName;
}
controller:
@PostMapping("/importExcel")
public void importExcel(@RequestPart("file") MultipartFile file){
apiTestService.importExcel(file);
}
serviceImpl:
@Override
public void importExcel(MultipartFile file) {
try {
ImportParams params = new ImportParams();
params.setHeadRows(2);
// 弄一个开始时间和结束时间,看看最后的执行效果怎么样
long startTime = new Date().getTime();
final long[] endTime = new long[1];
// 创建一个数组来保存导入的数据
LinkedList entities = new LinkedList();
ExcelImportUtil.importExcelBySax(file.getInputStream(), ImportEntity.class, params, new IReadHandler() {
@Override
public void handler(ImportEntity entity) {
// 在这里处理每条数据,因为这种导入没有返回值,所有我们可以在这里进行校验,并把通过的塞进 list 中
if (entity.getAddress() != null) {
entities.add(entity);
}
}
@Override
public void doAfterAll() {
// 里面的变量默认是数组的
endTime[0] = new Date().getTime();
}
});
// 输出下 list 的数量
System.out.println("导入的数据共有:" + entities.size() + "条");
// 计算一下导入的时间为
System.out.println("导入时间共计:" + (endTime[0] - startTime) + " ms");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
结果:
三、导出
3.1 简单导出
关键代码:ExcelExportUtil.exportExcel(ExportParams entity, Class pojoClass, Collection dataSet);
entity: 导入参数,常用的就是传入 title 和 sheetName,直接开 new
pojoClass: 导出文件内容的类的字节码类型
dataSet: 需要导出的数据
表:
导出实体类:
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExportEntity {
@Excel(name = "序号", needMerge = true)
private Integer serialNum;
@Excel(name = "姓名", needMerge = true)
private String name;
@Excel(name = "性别", needMerge = true, replace = {"男_true", "女_false"}, width = 15.0)
private Boolean sex;
@Excel(name = "电话", needMerge = true)
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss", needMerge = true, width = 20.0)
private Date buyTime;
@Excel(name = "住址", needMerge = true, width = 15.0)
private String address;
@Excel(name = "水果", groupName = "订单类别", width = 15.0)
private String fruitsName;
@Excel(name = "蔬菜", groupName = "订单类别", width = 15.0)
private String vegetableName;
}
controller:
@GetMapping(value = "/exportExcelTest")
public void exportExcelTest(HttpServletResponse response) {
apiTestService.exportExcelTest(response);
}
serviceImpl:
@Override
public void exportExcelTest(HttpServletResponse response) {
LinkedList entities = new LinkedList();
// 先造一些数据
for (int i = 1; i
结果:
简单导出的关键在于实体类的设置,需要根据表头去一点一点的去调整,贼拉麻烦,所以就有了模板导出
3.2 模板导出
关键代码:ExcelExportUtil.exportExcel(TemplateExportParams params, Map map);
params:模板参数,直接开 new,第一个参数是模板文件的位置,第二个参数是在第几个sheet 中导出,位置从 0 开始
map: 导出的内容数据
导出模板:
位置:
导出实体类:
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExportEntity {
private Integer serialNum;
private String name;
private Boolean sex;
private String phone;
private Date buyTime;
private String address;
private String fruitsName;
private String vegetableName;
}
serviceImpl:
@Override
public void exportExcelTest(HttpServletResponse response) {
// ================= 准备环节 ==================
LinkedList entities = new LinkedList();
// 先造一些数据
for (int i = 1; i
结果:
可以发现,模板导出的特点就是只填充表的数据,表的格式根本就不用管,甚至连实体类都是干净的一毛不拔,贼拉方便。里面的数据我们也可以在塞数据时进行微调:
// ================= 主要环节 ==================
// 获取模板数据
TemplateExportParams params = new TemplateExportParams("test/导出模板.xlsx", 0);
ArrayList list = new ArrayList();
for (ExportEntity entity : entities) {
// 塞入具体数据,工具类都是 hutool 包提供
Map map = BeanUtil.beanToMap(entity);
map.put("sex", entity.getSex() ? "男" : "女");
map.put("buyTime", DateUtil.format(entity.getBuyTime(), "yyyy-MM-dd HH:mm:ss"));
list.add(map);
}
HashMap workbookNeedMap = new HashMap();
workbookNeedMap.put("maps", list);
// 导出,并获取工作簿
Workbook workbook = ExcelExportUtil.exportExcel(params, workbookNeedMap);
其他的都不用变,结果:
完美~😎
3.3 多 sheet 的模板导出
关键代码:ExcelExportUtil.exportExcel(TemplateExportParams params, Map map);
params:模板参数,将第二个 sheet 参数设置为 true
map: 导出的内容数据,将多个 sheet 的 map 都添加进这里面
和 模板导出不同的地方就是将 导入的模板参数的 第二个设置为 true,然后不同的 sheet 表其表达式的 maps 设置成不同的参数就可以了。
表:
Role 实体类:
package web_test.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoleEntity {
private Integer id;
private String name;
private String type;
}
serviceImpl:
@Override
public void exportExcelTest(HttpServletResponse response) {
// ================= 准备环节 ==================
LinkedList entities = new LinkedList();
LinkedList roles = new LinkedList();
// 先造一些数据
for (int i = 1; i
结果:
真是妙~~啊
3.4 大数据导出
方法一:先查出数据,再导
核心代码:ExcelExportUtil.exportBigExcel(ExportParams entity, List excelParams);
entity:导出参数,直接开 new
excelParams: 导出数据
这种方法比较简单,他的特点是,先把数据查出来保存在 List 中,然后根据实体类的 @Excel 配置直接导出表
实体类:
package web_test.model;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author shenlongdaxia
*/
@EqualsAndHashCode(callSuper = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExportEntity {
@Excel(name = "序号", needMerge = true)
private Integer serialNum;
@Excel(name = "姓名", needMerge = true)
private String name;
@Excel(name = "性别", needMerge = true, replace = {"男_true", "女_false"}, width = 15.0)
private Boolean sex;
@Excel(name = "电话", needMerge = true)
private String phone;
@Excel(name = "购买时间", format = "yyyy-MM-dd HH:mm:ss", needMerge = true, width = 20.0)
private Date buyTime;
@Excel(name = "住址", needMerge = true, width = 15.0)
private String address;
@Excel(name = "水果", groupName = "订单类别", width = 15.0)
private String fruitsName;
@Excel(name = "蔬菜", groupName = "订单类别", width = 15.0)
private String vegetableName;
}
controller:
@GetMapping(value = "/exportExcelTest")
public void exportExcelTest(HttpServletResponse response) {
apiTestService.exportExcelTest(response);
}
serviceImpl:
@Override
public void exportExcelTest(HttpServletResponse response) {
// ================= 准备环节 ==================
LinkedList entities = new LinkedList();
// 先造一些数据
for (int i = 1; i
结果:
完美~😎
方法二:在导入的方法中查数据
关键代码:ExcelExportUtil.exportBigExcel(ExportParams entity, Class pojoClass,
IExcelExportServer server, Object queryParams);
entity:导出参数,还是基本导出的那两个,直接开 new
pojoClass:导出文件内容的类的字节码类型
server: 在这个 server 里面通过自身的业务层的分页逻辑去数据库获取大数据内容,比如通过mybatisPlus 的分页逻辑来获取内容数据
queryParams:server 分页查询内容的过滤条件
这种的导出效果和 方法一的导出效果是一样的,它利用的是 分页查询 来查的数据,大爷们可以看看下方链接,也挺好实现的💕(内心os:码不动了)
Easypoi-大数据量导出_easypoi 大数据导出-CSDN博客
【springboot+easypoi】大数据量excel导出 - 简书 (jianshu.com)






















