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 简单邮件传送…...
现代前端框架的发展与演进
现代前端框架的发展与演进是一个非常值得关注的话题,反映了整个前端生态系统的不断演化与技术深度的提升。以下是这一趋势的详细解析: 📈 现代前端框架的发展与演进 🔹 第一阶段:jQuery 时代(2006-2013&am…...
MySQL Binlog 数据恢复全指南
MySQL Binlog 数据恢复全指南 一、Binlog 核心概念 1. 什么是 Binlog? Binlog(二进制日志)是 MySQL 记录所有修改数据的 SQL 语句的日志文件,采用二进制格式存储。它是 MySQL 最重要的日志之一,具有三大核心功能&am…...

计算机视觉处理----OpenCV(从摄像头采集视频、视频处理与视频录制)
一、采集视频 VideoCapture 用于从视频文件、摄像头或其他视频流设备中读取视频帧。它可以捕捉来自 多种源的视频。 cv2.VideoCapture() 打开摄像头或视频文件。 cap cv2.VideoCapture(0) # 0表示默认摄像头,1是第二个摄像头,传递视频文件路径也可以 …...
DeepSeek 赋能智能养老:情感陪伴机器人的温暖革新
目录 一、引言二、智能养老情感陪伴机器人的市场现状与需求2.1 市场现状2.2 老年人情感陪伴需求分析 三、DeepSeek 技术详解3.1 DeepSeek 的技术特点3.2 与其他类似技术的对比优势 四、DeepSeek 在智能养老情感陪伴机器人中的具体应用4.1 自然语言处理与对话交互4.2 情感识别与…...

算法-多条件排序
1、数对排序的使用 pair<ll,ll> a[31];//cmp为比较规则 ll cmp(pair<ll,ll>a,pair<ll,ll>b){if(a.first!b.first)return a.first>b.first;else return a.second<b.second; }//按照比较规则进行排序 sort(a1,a31,cmp); 2、具体例题 输入样例࿱…...
AI推理服务的高可用架构设计
AI推理服务的高可用架构设计 在传统业务系统中,高可用架构主要关注服务冗余、数据库容灾、限流熔断等通用能力。而在AI系统中,尤其是大模型推理服务场景下,高可用架构面临更加复杂的挑战,如推理延迟敏感性、GPU资源稀缺性、模型版本切换频繁等问题。本节将专门探讨如何构建…...

3. 简述node.js特性与底层原理
😺😺😺 一、Node.js 底层原理(简化版) Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时,底层核心由几部分组成: 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行࿰…...

GC1808:高性能24位立体声音频ADC芯片解析
1. 芯片简介 GC1808 是一款24位立体声音频模数转换器(ADC),支持96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于家庭影院、蓝牙音箱等场景。 核心特性 高精度:24位分辨率,…...
tpc udp http
TCP(传输控制协议)、UDP(用户数据报协议)和 HTTP(超文本传输协议)是网络通信中常用的三种协议,它们在不同的层次和场景中发挥作用。以下是对这三种协议的详细解释以及它们之间的区别:…...

BugKu Web渗透之eval
启动场景,打开网页,显示的是一段代码。 步骤一: 分析代码。 代码大概意思是: <?php//包含"flag.php"的文件include "flag.php"; //获取网页请求的hello数据$a $_REQUEST[hello]; //显示变量a的详…...