SpringBoot整合EasyExcel实现读操作

目录
- 1、简单关系
- 2、准备数据
- 1、Excel表格:
- 2、实体类:
- 3、监听类
- 4、业务处理类
- 3、Demo说明
- 1、启动项目
- 2、查看控制台信息
- 4、数据转换、格式处理
- 5、Web操作,上传文件处理
- 6、泛型使用
- 1、修改Controller加入读取设备的Excel接口
- 2、修改业务类,加入设备的处理逻辑
- 3、建立设备Excel及Entity
- 4、修改监听类
- 5、发起请求进行测试
注意:项目没有搭建任何前端页面进行操作,所有的操作使用Swagger2进行模拟前端
1、简单关系
在读取Excel表格时,需要依赖一个Entity类或者直接是Map类型,并且对应一个Listener监听类,在监听类中通过继承AnalysisEventListener实现方法,对数据进行处理操作。
以下demo主要参考官方的操作,在其中加入一些实际开发中的需求和想法。
建议参考EasyExcel官方文档再结合看Demo,EasyExcel
2、准备数据
1、Excel表格:

2、实体类:
/*** @Date: 2020/5/1 15:13* @Description: 人员实体类**/
@Data
//注意属性的顺序对应excel中各个列解析的顺序,为了处理单元格为空的情况莫须有写一个Converter类,对空进行判断,并且返回默认值
public class PersonData {//名称private String name;//入职时间private String date;//部门private String dept;//年薪private Double year = 0.00;//月薪private Double month = 0.00;
}
3、监听类
官方的监听类没有交给Spring管理,下列demo中将监听类交给Spring进行管理;并且结合一点业务处理进行构建Demo。
@Slf4j
@Component
public class PersonDataListener extends AnalysisEventListener<PersonData> {//引入业务处理的Server@Resourceprivate ReadExcelService excelService;//存放解析后的数据private List<PersonData> list = new ArrayList<>();/*** 读取每一条头部信息** @param headMap* @param context*/@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}/*** 这个每一条数据解析都会来调用** @param context*/@Overridepublic void invoke(PersonData data, AnalysisContext context) {//调用业务类,对数据进行校验excelService.checkPersonExcel(data, rowIndex);list.add(data);log.info("解析到一条数据:{}", JSON.toJSONString(data));}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", list.size());List<PersonData> dataList = new ArrayList<>();//调用service储存数据excelService.savePersonExcel(dataList);list.clear();}
}
4、业务处理类
@Service
@Slf4j
public class ReadExcelService {/*** 每隔3000条存储数据库,然后清理list ,方便内存回收*/@Resourceprivate PersonDataListener personDataListener;//读取用户选择的excel文件public void readPeronExcel(String filePath) {//设置headRowNumber表示从第几行开始读取,下标从0开始// 写法1:// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭,因为excel第三行开始 才是数据EasyExcel.read(filePath, PersonData.class, personDataListener).sheet().headRowNumber(2).doRead();/*// 写法2:ExcelReader excelReader = EasyExcel.read(filePath, PersonData.class, new PersonDataListener()).build();//指定读取的SheetReadSheet readSheet = EasyExcel.readSheet(0).headRowNumber(2).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();*/}public void readPeronExcelByUpload(MultipartFile file) throws Exception {//设置headRowNumber表示从第几行开始读取,下标从0开始EasyExcel.read(file.getInputStream(), PersonData.class, personDataListener).sheet().headRowNumber(2).doRead();}//对导入数据进行业务判断public void checkPersonExcel(PersonData personData, Integer rowIndex) {if (StringUtils.isEmpty(personData.getName()) || StringUtils.isEmpty(personData.getDept()) || StringUtils.isEmpty(personData.getDate())) {throw new ServiceException(500, "第" + rowIndex + "行,数据不能为空");}}//保存有效的导入数据public void savePersonExcel(List<PersonData> list) {//对数据进行一些业务处理,比如将月薪大于5000的数据,分出来List<PersonData> collect = list.stream().filter(li -> li.getMonth() >= 5000).collect(Collectors.toList());log.info("月薪达标按数量:" + collect.size());//插入数据库log.info("插入人员数据库:" + list.size());}}
3、Demo说明
1、启动项目
访问:http://127.0.0.1:8080/demo/doc.html,然后选择录入用户文件路径功能,在地址栏录入excel路径,点击发送进行请求

2、查看控制台信息
控制台打印信息如下:

可以看到 数据解析已经成功。
4、数据转换、格式处理
上面的Demo中,年薪和月薪是Doubl的类型;但是,你不能保证用户录入的Excel中数据格式一定是数字类型,可能会出现字符串类型的情况,直接进行导入会出现转换异常
我们需要自定义一个转换类,实现无论什么格式,都进行Double类型的输出
public class CustomDoubleConverter implements Converter<Double> {@Overridepublic Class supportJavaTypeKey() {return Double.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.NUMBER;}/*** 这里读的时候会调用** @param cellData NotNull* @param contentProperty Nullable* @param globalConfiguration NotNull* @return*/@Overridepublic Double convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {CellDataTypeEnum type = cellData.getType();double newValue = 0.00d;if (type == CellDataTypeEnum.STRING) {//如果为文本则转换为为DoublenewValue = Double.valueOf(cellData.getStringValue());} else {newValue = Double.valueOf(cellData.getNumberValue().toString());}return newValue;}/*** 这里是写的时候会调用** @param value NotNull* @param contentProperty Nullable* @param globalConfiguration NotNull* @return*/@Overridepublic CellData convertToExcelData(Double value, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {return new CellData(value);}
}
并且在Entity中字段上加入注解:@ExcelProperty(converter = CustomDoubleConverter.class);经常也会出现日期格式不统一的情况,可以使用 @DateTimeFormat("yyyy-MM-dd")进行统一的日期格式化操作。
修改Entity如下:
@Data
//注意属性的顺序对应excel中各个列解析的顺序,为了处理单元格为空的情况莫须有写一个Converter类,对空进行判断,并且返回默认值
public class PersonData {//名称private String name;//入职时间,日期格式化操作(不用关系excel中的日期为文本还是自定义日期格式)@DateTimeFormat("yyyy-MM-dd")//只会转换excel中的时间格式,如果excel本来就是String类型,需要写一个转换器进行统一的转换private String date;//部门private String dept;//年薪,对excel里面的数据进行统一的转换,当解析到年薪单元格时,会CustomDoubleConverter中进行格式转换操作@ExcelProperty(converter = CustomDoubleConverter.class)private Double year = 0.00;//月薪,对excel里面的数据进行统一的转换@ExcelProperty(converter = CustomDoubleConverter.class)private Double month = 0.00;
}
5、Web操作,上传文件处理
直接通过输入流进行读取excel操作
public void readPeronExcelByUpload(MultipartFile file) throws Exception {//设置headRowNumber表示从第几行开始读取,下标从0开始EasyExcel.read(file.getInputStream(), PersonData.class, personDataListener).sheet().headRowNumber(2).doRead();}
6、泛型使用
如果我们存在导入多个excel每个excel对应不同的Entity类和不同的业务操作,那么需要开发者创建多个Entity类(这是应该的),但是也需要创建多个监听类,并且监听类的代码几乎一样,只有少量的业务处理不一致,那么每次创建多个监听类,显得很没必要。为了解决这个问题 我们可以使用泛型进行处理。
1、修改Controller加入读取设备的Excel接口
@GetMapping("/device")@ApiOperation(value = "录入设备文件路径", notes = "录入设备文件路径")@ApiImplicitParam(name = "filePath", value = "Excel路径", paramType = "query", required = true, dataType = "String")public Object readDevice(@RequestParam String filePath) {readExcelService.readDeviceExcel(filePath);return ResponseObject.success();}
2、修改业务类,加入设备的处理逻辑
//读取设备的excel文件public void readDeviceExcel(String filePath) {EasyExcel.read(filePath, PersonData.class, personDataListener).sheet().headRowNumber(2).doRead();}//保存有效的导入数据public void saveDeviceExcel(List<DeviceData> list) {//插入数据库log.info("插入设备数据库:" + list.size());}
3、建立设备Excel及Entity

@Data
public class DeviceData {//名称private String name;//设备编码private String code;//设备价格private String price;
}
4、修改监听类
在原来的监听类中,需要指定到一个Entity上,现在我们直接使用泛型 T,来标识任何一个实体类,通过instanceof判断传入的泛型属于那个Entity。在service中使用EasyExcel.read()方法时,一定要指定实体的类型,所以不同的excel要对应不同的EasyExcel.read()方法。
@Slf4j
@Component
public class PersonDataListener<T> extends AnalysisEventListener<T> {@Resourceprivate ReadExcelService excelService;//存放解析后的数据private List<T> list = new ArrayList<>();/*** 读取每一条头部信息** @param headMap* @param context*/@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}/*** 这个每一条数据解析都会来调用** @param t one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(T t, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(t));//调用service类进行一些业务判断操作Integer rowIndex = context.readRowHolder().getRowIndex() + 1;//当时行数,从0开始,实际加1//通过判断泛型类,调用不同的Service类if (t instanceof PersonData) {PersonData data = (PersonData) t;excelService.checkPersonExcel(data, rowIndex);list.add(t);}if (t instanceof DeviceData) {list.add(t);}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!");}/*** 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。** @param exception* @param context* @throws Exception*/@Overridepublic void onException(Exception exception, AnalysisContext context) {log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());// 如果是某一个单元格的转换异常 能获取到具体行号// 如果要获取头的信息 配合invokeHeadMap使用if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),excelDataConvertException.getColumnIndex());}}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", list.size());//人员数据if (list instanceof PersonData) {List<PersonData> dataList = new ArrayList<>();excelService.savePersonExcel(dataList);}//设备数据if (list instanceof PersonData) {List<DeviceData> dataList = new ArrayList<>();excelService.saveDeviceExcel(dataList);}list.clear();}
}
5、发起请求进行测试

依次进行请求发送,可以看到控制台输出不一样,但是监听类却只有一个

具体代码文档描述不清楚,可以下载Demo进行运行看效果。
读写操作Github:https://github.com/lxlhz/easy-demo

相关文章:
SpringBoot整合EasyExcel实现读操作
😊 作者: 一恍过去 💖 主页: https://blog.csdn.net/zhuocailing3390 🎊 社区: Java技术栈交流 🎉 主题: SpringBoot整合EasyExcel实现读操作 ⏱️ 创作时间: 2023年…...
go mod 设置国内源 windows 环境 win10
启用 go module 功能 go env -w GO111MODULEon 配置 goproxy 变量 go env -w GOPROXYhttps://goproxy.cn,direct 下载包就行了,速度飞快 go mod tidy 检测 goproxy 是否配置好 运行 go env | findstr goproxy 查看 goproxy Go module 从 Go v1.12 版本开始存在&a…...
智能决策支持系统实现的关键技术分析
1.模 型 中 的 关 键 因 素 。 在 按 本 模 型 研 究 开 发 系 统 时 ,应 当 着 重 考 虑 以 下 几 个 因 素 : (1)设 备 保 护 需 求 计 划 。 保 护 需 求 包 括 了 人 员 、 物 质 、 财 务 等 各项 因 素 ; (2)考 虑 设 备 运行 及维修的 历史数据。 这是进 行 模 型 选 择…...
OSS对象存储后端实现+Vue实现图片上传【基于若依管理系统开发】
文章目录 基本介绍术语介绍图片上传方式介绍普通上传用户直传应用服务器签名后直传 OSS对象存储后端实现maven配置文件配置类ServiceController 图片上传前端图片上传组件api页面使用组件组件效果 基本介绍 术语介绍 Bucket(存储空间):用于…...
人工智能学习目录
1、人工智能-电脑如何像人一样思考? 从发展历史到人工智能的应用案例,再到人工智能本质是数学问题,从房价预测问题提出损失函数由参数导致,再由损失函数的最优值入手引入梯度下降法,最后到多参数方程的最优求解。 人工…...
Vue单页面实现el-tree el-breadcrumb功能、el-tree右键点击树节点展示菜单功能、树节点编辑节点字段名称功能
(1) 点击el-tree节点 使用el-breadcrumb展示选中树节点及父项数据 重点:handleNodeClick方法、getTreeNode方法 (2) 选择el-breadcrumb-item设置el-tree节点选中 必须设置属性: current-node-key"currentNodeKey" 、 node-key"id" 重点: 设置…...
C++核心编程之函数高级使用
目录 一、函数的默认参数 二、函数占位参数 三、函数重载 四、函数重载-注意事项 一、函数的默认参数 在C中,函数的形参列表中的形参是可以有默认值的 语法:返回值类型 函数名 (参数默认值){} 示例1: #includ…...
如何创建智能合约游戏系统
区块技术的发展,智能合约成为了一个热门话题。智能合约是一种基于区块技术的自动化合约,它可以自动执行合同中规定的条款,从而实现去中心化的信任和价值传递。在游戏领域,智能合约可以让玩家在游戏中实现各种交易和交互࿰…...
如何用rust实现一个异步channel
目录 前言思路实现功能代码实现 测试先引测试版包测试代码结果与分析思考 尾语 前言 使用通信来共享内存,而不是通过共享内存来通信 上面这句话,是每个go开发者在 处理多线程通信时 的座右铭,go甚至把实现这个理念的channel直接焊在编译器里&…...
gitee上传项目到仓库
目录 一些常用的Git命令切换到其他盘符:列出目录下的所有文件:以树状图的形式显示目录下的文件和子目录:返回上一层目录:写的C#代码文件上传到新建的Git仓库中,可以按照以下步骤进行操作:出现的错误: 一些常…...
day27 贪心算法
1.什么是贪心? 比如10张钞票,有1,5,20,100等面额,取五张,如何取得到数额最多的钱?每次取面额最大的那张钞票;就是每个阶段的局部最优;全局最优就是最后拿到的…...
Java实现字符串反转
起因 自己在刷题的过程中,想把一个字符串翻转一下,便写了下面的代码: String str "abcd";str str.reverse();发现行不通,这是为什么呢? 分析 在Java中,字符串是不可变的对象,这意…...
vue - 常见的性能优化
文章目录 vue使用中常见的性能优化1, v-for 遍历避免同时使用 v-if2, 如果需要使用v-for给每项元素绑定事件时 可以使用事件代理**3, 一些数据不做响应式4,一些页面采用keep-alive缓存组件5,第三方UI库按需导入6&#…...
微服务系列文章 之 Nginx服务状态监控的方法
在Nginx的插件模块中有一个模块stub_status可以监控Nginx的一些状态信息,默认安装可能没有这个模块,手动编译的时候加一下即可。 1. 模块安装 先使用命令查看是否已经安装这个模块: [rootihxb123Z nginx]# ./nginx -V (V大写会显示版本号和…...
【网络系统集成】路由器实验
1.实验名称:路由器RIP协议配置 2.实验目的 在PacketTracer中进行模拟实验,配置RIP协议,验证RIP协议更新时间及路由状态变化,加深对路由器RIP协议相关知识的理解与掌握。 3.实验内容 (1)拓扑结构图 (2)ip地址分配与端口分配...
【mac 安装Miniconda】
1.下载Miniconda 注意mac是什么版本,m1下载m1版本 https://docs.conda.io/en/latest/miniconda.html#macos-installers 2.安装Miniconda 在下载文件所在目录下打开终端,输入一下命令: bash Miniconda3-latest-MacOSX-x86_64.sh 一路回车&…...
螺栓疲劳计算-风电行业,参考GL2010, ST0361,1993-1-9
由于不想再重新排版了,于是转成了图片。...
QT学习之旅 - QThread多线程
文章目录 首先是主线程 其次是一个程序 通过一个QThread来放入程序 进阶一点: 手动开启关闭线程俩个线程 其实QT中的thread(线程)是很容易的 首先是主线程 #include "mainwindow.h" #include "ui_mainwindow.h"#include <QDebug>MainWindow::MainWin…...
PROFINET转TCP/IP网关TCP/IP协议的含义是
大家好,今天要和大家分享一款自主研发的通讯网关,远创智控YC-PN-TCPIP。这款网关可是集多种功能于一身,PROFINET从站功能,让它在通讯领域独领风骚。想知道这款网关如何实现PROFINET和TCP/IP网络的连接吗?一起来看看吧&…...
计算机网络基础第六章
一、应用层概述 1.1 网络应用模型 1.1.1 客户/服务器(C/S)模型 1.1.2 P2P模型 二、域名解析系统——DNS系统 2.1 域名 2.2 域名服务器 2.3 域名解析过程 三、文件传输协议——FTP 3.1 FTP服务器和用户端 3.2 FTP工作原理 四、电子邮件 4.1 电子邮件系统概述 4.2 简单邮件传送…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
python打卡day49@浙大疏锦行
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...
