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 简单邮件传送…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...