当前位置: 首页 > news >正文

SpringBoot集成阿里easyexcel(二)Excel监听以及常用工具类

EasyExcel中非常重要的AnalysisEventListener类使用,继承该类并重写invoke、doAfterAllAnalysed,必要时重写onException方法。

Listener 中方法的执行顺序


首先先执行 invokeHeadMap() 读取表头,每一行都读完后,执行 invoke()方法 读取数据,最后是doAfterAllAnalysed() 方法。
invoke通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据。对当前获取的行的数据配合实体类字段的校验注解来实现数据校验,并返回错误提示,也可校验表头(相应index下表对应excel中的中文名称)是否正确。
doAfterAllAnalysed方法可定义在资源解析完成之后的操作 onException方法定义在数据解析出错失败时的操作,一般为当前数据行的数据无法与ExcelProperty所注解的字段类型想符合,从而导致无法继续解析流沉的错误。


一、ExcelListener监听器

public class ExcelListener extends AnalysisEventListener {/*** 自定义用于暂时存储data。* 可以通过实例获取该值*/private List<Object> datas = new ArrayList<>();private List<ImportErrVo> errDatas = new ArrayList<>();private Map<Integer, String> headMap = new HashMap<>();private Boolean head = false;/*** 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据*/@Overridepublic void invoke(Object object, AnalysisContext context) {checkHead(object);//属性验证int rowNum = context.readRowHolder().getRowIndex();String errMsg;try {errMsg = EasyExcelValiHelper.validateEntity(object);} catch (Exception e) {errMsg = "解析数据出错";e.printStackTrace();}if (!StringUtils.isEmpty(errMsg)) {ImportErrVo errVo = new ImportErrVo();errVo.setLine(rowNum + 1 + "");errVo.setErrMsg(errMsg);errDatas.add(errVo);} else {//数据存储到list,供批量处理,或后续自己业务逻辑处理。datas.add(object);}//根据业务自行 dosomethingdoSomething();}/*** 根据业务自行实现该方法*/private void doSomething() {}/*** 校验表头判断是否传错** @param object*/public void checkHead(Object object) {Field[] fields = object.getClass().getDeclaredFields();if (head) {return;}for (Field f : fields) {if (f.getName().equals("serialVersionUID")) {continue;}ExcelProperty excelProperty = f.getAnnotation(ExcelProperty.class);int index = 0;if (excelProperty!=null){index= excelProperty.index();}if ((excelProperty != null && headMap.get(index)==null) || (excelProperty != null && !headMap.get(index).equals(excelProperty.value()[excelProperty.value().length-1]))) {ImportErrVo errVo = new ImportErrVo();errVo.setLine("1");errVo.setErrMsg("表头数据不对应,导入数据失败");errDatas.add(errVo);this.head = true;break;}}}/*** 解析结束的操作** @param object*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {if (head) {errDatas.removeIf(a -> !a.getLine().equals("1"));datas.clear();}/*datas.clear();解析结束销毁不用的资源*/}/*** 解析出错时的操作** @param object*/@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {int rowIndex = context.readRowHolder().getRowIndex();int headLineNumber = context.readSheetHolder().getHeadRowNumber();if (rowIndex == headLineNumber) {Object object = null;try {object = context.currentReadHolder().excelReadHeadProperty().getHeadClazz().newInstance();} catch (Exception e) {ImportErrVo errVo = new ImportErrVo();errVo.setLine("1");errVo.setErrMsg("表头数据不对应,导入数据失败");errDatas.add(errVo);this.head = true;}if (object != null) {checkHead(object);}}if (head) {return;}String error;if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;CellData cellData = excelDataConvertException.getCellData();error = cellData.toString();} else {String eMassage = exception.getCause().toString();if (eMassage.contains("\"")) {error = eMassage.substring(eMassage.lastIndexOf(':') + 2);} else {error = eMassage.substring(eMassage.lastIndexOf(':') + 1);}}LinkedHashMap<Integer, CellData> content = (LinkedHashMap<Integer, CellData>) context.readRowHolder().getCurrentRowAnalysisResult();error = error.replaceAll("\"", "");ImportErrVo errVo = new ImportErrVo();for (Map.Entry<Integer, CellData> map : content.entrySet()) {CellDataTypeEnum type = map.getValue().getType();String c = null;if (type.equals(CellDataTypeEnum.NUMBER)) {c = map.getValue().getNumberValue().toString();} else if (type.equals(CellDataTypeEnum.STRING)) {c = map.getValue().getStringValue();} else if (type.equals(CellDataTypeEnum.BOOLEAN)) {c = map.getValue().getBooleanValue().toString();}if (error.equals(c)) {errVo.setLine(rowIndex + 1 + "");errVo.setErrMsg(headMap.get(map.getKey()) + "数据格式错误");break;}}boolean flag = true;for (ImportErrVo importErrVo : errDatas) {if (importErrVo.getLine().equals(errVo.getLine())) {importErrVo.setErrMsg(importErrVo.getErrMsg() + errVo.getErrMsg() + ";");flag = false;break;}}if (flag) {errDatas.add(errVo);}}@Overridepublic void invokeHeadMap(Map headMap, AnalysisContext context) {this.headMap = headMap;}public List<Object> getDatas() {return datas;}public void setDatas(List<Object> datas) {this.datas = datas;}public List<ImportErrVo> getErrDatas() {return errDatas;}public void setErrDatas(List<ImportErrVo> errDatas) {this.errDatas = errDatas;}}

二、初步检验导入参数

在实体类中可用: javax.validation.constraints下的注解对字段进行校验
EasyExcelValiHelper类(初步校验字段)

public class EasyExcelValiHelper {private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();public static <T> String validateEntity(T obj) throws NoSuchFieldException, SecurityException {StringBuilder result = new StringBuilder();Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class);if (set != null && set.size() != 0) {for (ConstraintViolation<T> cv : set) {Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);result.append(annotation.value()[0]+cv.getMessage()).append(";");}}return result.toString();}
}

三、ExcelUti类

public class ExcelUtil {/*** 读取某个 sheet 的 Excel** @param excel 文件* @param head* @param sheetNo sheet 的序号 从1开始* @return Excel 数据 list*/public static List<Object> readExcel(MultipartFile excel, Class head, int sheetNo) {return readExcel(excel, head, sheetNo, 1);}/*** 读取某个 sheet 的 Excel** @param excel 文件* @param head* @param sheetNo sheet 的序号 从1开始* @return Excel 数据 list*/public static List<Object> readExcel(MultipartFile excel, Class head, int sheetNo, ExcelListener excelListener) {ExcelReader reader = getReader(excel, head,excelListener);if (reader == null) {return null;}ReadSheet readSheet = EasyExcel.readSheet(sheetNo).build();reader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的reader.finish();return excelListener.getDatas();}/*** 读取某个 sheet 的 Excel** @param excel 文件* @param head* @param sheetNo sheet 的序号 从1开始* @param headLineNum 表头行数,默认为1* @return Excel 数据 list*/public static List<Object> readExcel(MultipartFile excel,Class head, int sheetNo, int headLineNum) {ExcelListener excelListener = new ExcelListener();ExcelReader reader = getReader(excel, head,excelListener);if (reader == null) {return null;}ReadSheet readSheet = EasyExcel.readSheet(sheetNo).headRowNumber(headLineNum).build();reader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的reader.finish();return excelListener.getDatas();}/*** 读取某个 sheet 的 Excel** @param excel 文件* @param head* @param sheetNo sheet 的序号 从1开始* @param headLineNum 表头行数,默认为1* @return Excel 数据 list*/public static List<Object> readExcel(MultipartFile excel,Class head, int sheetNo, int headLineNum,ExcelListener excelListener) {ExcelReader reader = getReader(excel, head,excelListener);if (reader == null) {return null;}ReadSheet readSheet = EasyExcel.readSheet(sheetNo).headRowNumber(headLineNum).build();reader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的reader.finish();return excelListener.getDatas();}/*** 返回 ExcelReader** @param excel 需要解析的 Excel 文件* @param excelListener new ExcelListener()*/private static ExcelReader getReader(MultipartFile excel, Class head, AnalysisEventListener excelListener){String filename = excel.getOriginalFilename();if (filename == null || (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {return null;}InputStream inputStream;try {inputStream = new BufferedInputStream(excel.getInputStream());ExcelReader excelReader =  EasyExcel.read(inputStream,head,excelListener).build();return excelReader;} catch (IOException e) {e.printStackTrace();}return null;}/*** 判断你一个类是否存在某个属性(字段)** @param fieldName 字段* @param obj   类对象* @return true:存在,false:不存在, null:参数不合法*/public static Boolean isExistField(String fieldName, Object obj) {if (obj == null || StringUtils.isEmpty(fieldName)) {return null;}//获取这个类的所有属性Field[] fields = obj.getClass().getDeclaredFields();boolean flag = false;//循环遍历所有的fieldsfor (int i = 0; i < fields.length; i++) {if (fields[i].getName().equals(fieldName)) {flag = true;break;}}return flag;}private static ExcelReader getReaderNoHead(MultipartFile excel, AnalysisEventListener excelListener){String filename = excel.getOriginalFilename();if (filename == null || (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {return null;}InputStream inputStream;try {inputStream = new BufferedInputStream(excel.getInputStream());ExcelReader excelReader =  EasyExcel.read(inputStream,excelListener).build();return excelReader;} catch (IOException e) {e.printStackTrace();}return null;}/*** 新增或修改合并策略map* @param strategyMap* @param key* @param index*/private static void fillStrategyMap(Map<String, List<RowRangeVo>> strategyMap, String key, int index) {List<RowRangeVo> rowRangeDtoList = strategyMap.get(key) == null ? new ArrayList<>() : strategyMap.get(key);boolean flag = false;for (RowRangeVo dto : rowRangeDtoList) {//分段list中是否有end索引是上一行索引的,如果有,则索引+1if (dto.getEnd() == index) {dto.setEnd(index + 1);flag = true;}}//如果没有,则新增分段if (!flag) {rowRangeDtoList.add(new RowRangeVo(index, index + 1));}strategyMap.put(key, rowRangeDtoList);}
}

五、controller中的使用(导入时解析成列表数据并获得错误提示)

    @PostMapping("/import")@ApiOperation("导入XX信息")public ResponseResult<?> importProject(@RequestParam("file") MultipartFile file)  throws Exception{List<ProjectExpView> list = new ArrayList<>(1);List<ImportErrVo> errMsgList = new ArrayList<>(1);ExcelListener excelListener = new ExcelListener();Object Object1 = ExcelUtil.readExcel(file,ProjectExpView.class,0,excelListener);errMsgList = excelListener.getErrDatas();list = (List<ProjectExpView>) Object1;projectService.importProject(list);if(null!=errMsgList&&errMsgList.size()>0){return ResponseResult.error(HttpStatus.NOT_IMPLEMENTED,"部分导入成功:成功"+(null==list?0:list.size())+"条,失败"+errMsgList.size()+"条",errMsgList);}return ResponseResult.importSuccess();}

参考:https://blog.csdn.net/her_he/article/details/125297884?spm=1001.2014.3001.5502
该作者主页还有其他easyExcel相关,感兴趣可以看看

相关文章:

SpringBoot集成阿里easyexcel(二)Excel监听以及常用工具类

EasyExcel中非常重要的AnalysisEventListener类使用&#xff0c;继承该类并重写invoke、doAfterAllAnalysed&#xff0c;必要时重写onException方法。 Listener 中方法的执行顺序 首先先执行 invokeHeadMap() 读取表头&#xff0c;每一行都读完后&#xff0c;执行 invoke()方法…...

使用ELK Stack进行日志管理和分析:从入门到精通

在现代IT运维中&#xff0c;日志管理和分析是确保系统稳定性和性能的关键环节。ELK Stack&#xff08;Elasticsearch, Logstash, Kibana&#xff09;是一个强大的开源工具集&#xff0c;广泛用于日志收集、存储、分析和可视化。本文将详细介绍如何使用ELK Stack进行日志管理和分…...

前端框架对比与选择

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f495; 目…...

Springboot jPA+thymeleaf实现增删改查

项目结构 pom文件 配置相关依赖&#xff1a; 2.thymeleaf有点类似于jstlel th:href"{url}表示这是一个链接 th:each"user : ${users}"相当于foreach&#xff0c;对user进行循环遍历 th:if进行if条件判断 {变量} 与 ${变量}的区别: 4.配置好application.ym…...

【YashanDB知识库】yashandb执行包含带oracle dblink表的sql时性能差

本文内容来自YashanDB官网&#xff0c;具体内容请见https://www.yashandb.com/newsinfo/7396959.html?templateId1718516 问题现象 yashandb执行带oracle dblink表的sql性能差&#xff1a; 同样的语句&#xff0c;同样的数据&#xff0c;oracle通过dblink访问远端oracle执行…...

效率工具推荐 | 高效管理客服中心知识库

人工智能AI的广泛应用&#xff0c;令AI知识库管理已成为优化客服中心运营的核心策略之一。一个高效、易用且持续更新的知识库不仅能显著提升客服代表的工作效率&#xff0c;还能极大提升客户的服务体验。而高效效率工具如HelpLook&#xff0c;能够轻松搭建AI客服帮助中心&#…...

综合实验1 利用OpenCV统计物体数量

一、实验简介 传统的计数方法常依赖于人眼目视计数&#xff0c;不仅计数效率低&#xff0c;且容易计数错误。通常现实中的对象不会完美地分开&#xff0c;需要通过进一步的图像处理将对象分开并计数。本实验巩固对OpenCV的基础操作的使用&#xff0c;适当的增加OpenCV在图像处…...

[Redis][主从复制][上]详细讲解

目录 0.前言1.配置1.建立复制2.断开复制3.安全性4.只读5.传输延迟 2.拓扑1.一主一从结构2.一主多从结构2.树形主从结构 0.前言 说明&#xff1a;该章节相关操作不需要记忆&#xff0c;理解流程和原理即可&#xff0c;用的时候能自主查到即可主从复制&#xff1f; 分布式系统中…...

【算法】leetcode热题100 146.LRU缓存. container/list用法

https://leetcode.cn/problems/lru-cache/description/?envTypestudy-plan-v2&envIdtop-100-liked 实现语言&#xff1a;go lang LRU 最近最少未使用&#xff0c;是一种淘汰策略&#xff0c;当缓存空间不够使用的时候&#xff0c;淘汰一个最久没有访问的存储单元。目前…...

[论文总结] 深度学习在农业领域应用论文笔记13

文章目录 1. Downscaling crop production data to fine scale estimates with geostatistics and remote sensing: a case study in mapping cotton fibre quality &#xff08;Precision Agriculture &#xff0c;2024&#xff0c; IF5.585&#xff09;背景方法结果结论个人总…...

《Detection of Tea Leaf Blight in Low-Resolution UAV Remote Sensing Images》论文阅读

学习资料 论文题目&#xff1a;Detection of Tea Leaf Blight in Low-Resolution UAV Remote Sensing Images&#xff08;低分辨率UAV遥感图像中茶叶枯萎病的检测&#xff09;论文地址&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber10345618 Abstr…...

低代码BPA(业务流程自动化)技术探讨

一、BPA流程设计平台的特点 可视化设计工具 大多数BPA流程设计平台提供直观的拖拽式界面&#xff0c;用户可以通过图形化方式设计、修改及优化业务流程。这种可视化的方式不仅降低了门槛&#xff0c;还便于非技术人员理解和参与流程设计。集成能力 现代BPA平台通常具备与其他系…...

开闭原则(OCP)

开闭原则&#xff08;OCP&#xff09;&#xff1a;Open Closed Princide&#xff1a;对扩展开放&#xff0c;对修改关闭。在程序需要进行拓展的时候&#xff0c;不能去修改原有代码&#xff0c;实现一个热插拔的效果。 简言之&#xff0c;是为了使程序的扩展性更好&#xff0c;…...

Unity之 TextMeshPro 介绍

TextMeshPro 是 Unity 中用于处理文本显示的高级插件&#xff0c;旨在替代 Unity 内置的 UI.Text 和 TextMesh 组件。与默认的文本组件相比&#xff0c;TextMeshPro 提供了更高的文本渲染质量和更多的文本样式选项&#xff0c;同时具备强大的优化能力。 TextMeshPro 的主要特点…...

Linux套接字Socket

Linux套接字Socket 前提知识补充 为不同机器上的两个进程之间提供通信机制 主机字节序小端存储,网络字节序大端存储 特点TCPUDP连接类型面向连接无连接可靠性高低有序性保证数据包按顺序到达不保证数据包顺序流量控制有滑动窗口机制无拥塞控制有拥塞控制机制无复杂性较高较低…...

基于 Web 的工业设备监测系统:非功能性需求与标准化数据访问机制的架构设计

目录 案例 【说明】 【问题 1】(6 分) 【问题 2】(14 分) 【问题 3】(5 分) 【答案】 【问题 1】解析 【问题 2】解析 【问题 3】解析 相关推荐 案例 阅读以下关于 Web 系统架构设计的叙述&#xff0c;回答问题 1 至问题 3 。 【说明】 某公司拟开发一款基于 Web 的…...

【MySQL】基础入门篇

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;理解什么是MySQL&#xff0c;如何安装MySQL&#xff0c;简单使用MySQL。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不…...

uni-app vue3封装websocket,支持微信小程序

一、创建useWebSocket.js 文件 // useWebSocket.js // 获取链接的URL前缀 import {BASE_URL } from "./request";import {ref,onMounted,onBeforeUnmount } from "vue";// 假设我们使用 uni-app 的 globalData 或 Vuex 来管理用户状态 // 这里为了简单起…...

杭州算力小镇:AI泛化解锁新机遇,探寻AI Agent 迭代新路径

人工智能技术不断迭代&#xff0c;重点围绕着两个事情&#xff0c;一是数据&#xff0c;二是算力。 算法的迭代推动着AI朝向多模态的方向发展&#xff0c;使之能够灵活应对不同领域的不同任务&#xff0c;模型的任务执行能力大大提升&#xff0c;人工智能泛化能力被推上高潮。…...

IT行业的现状与发展趋势

IT行业的现状与发展趋势 随着信息技术的迅速发展&#xff0c;IT行业已成为全球经济的重要支柱之一。无论是传统行业的数字化转型&#xff0c;还是新兴技术的快速崛起&#xff0c;IT行业都在不断推动社会的进步和发展。本文将探讨IT行业的现状及未来发展趋势。 IT行业的现状 …...

推荐8款AI辅助论文写作工具(如爱毕业aibiye)与入门使用教程

人工智能技术在学术研究中的深度整合&#xff0c;显著优化了学术论文的创作效能与成果质量。通过文献智能分析、语义生成引擎和语言优化算法等核心技术&#xff0c;8款前沿工具系统覆盖了知识图谱构建、学术内容生成、多维度文本增强等核心研究场景。这些智能化平台基于深度学习…...

K8s定时任务实战:如何用CronJob每分钟输出Hello World(附表达式详解)

K8s定时任务实战&#xff1a;从Hello World到生产级CronJob配置 在云原生技术栈中&#xff0c;定时任务作为自动化运维的核心组件&#xff0c;其重要性不言而喻。Kubernetes提供的CronJob资源&#xff0c;让开发者能够以声明式的方式管理周期性任务&#xff0c;而无需依赖传统…...

从硅片到电路:图解CMOS反相器的制造工艺与工作原理

从硅片到电路&#xff1a;图解CMOS反相器的制造工艺与工作原理 在半导体工业中&#xff0c;CMOS反相器作为数字电路的基本构建模块&#xff0c;其制造工艺凝聚了现代微电子技术的精华。本文将带您深入半导体fab的微观世界&#xff0c;通过工艺截面图的逐步解析&#xff0c;揭示…...

human-pose-estimation.pytorch:简单而强大的人体姿态估计终极指南

human-pose-estimation.pytorch&#xff1a;简单而强大的人体姿态估计终极指南 【免费下载链接】human-pose-estimation.pytorch The project is an official implement of our ECCV2018 paper "Simple Baselines for Human Pose Estimation and Tracking(https://arxiv.o…...

AUTOSAR NM实战避坑:从CANoe仿真到实车调试,搞定ECU异常唤醒与睡眠失败

AUTOSAR NM实战避坑指南&#xff1a;从仿真到实车的异常唤醒与睡眠失败解决方案 当ECU在深夜本该沉睡时突然"睁眼"&#xff0c;消耗的不仅是电量&#xff0c;更是工程师的睡眠时间。这种场景在AUTOSAR网络管理&#xff08;NM&#xff09;开发中屡见不鲜——某个节点异…...

03 MongoDB文档的各种增加、更新、删除操作总结

更多内容请见: 《深入掌握MongoDB数据库》 - 专栏介绍和目录 一. 插入文档 注意: 在 MongoDB 中,直接插入内容会自动创建集合! 1.1 使用insert()方法 语法格式: db.COLLECTION_NAME.insert(document) 说明: 若插入的数据主键已经存在,则会抛 org.springframework.dao.Du…...

Pixel Epic · Wisdom Terminal 部署与压测:使用.accelerate库优化推理性能

Pixel Epic Wisdom Terminal 部署与压测&#xff1a;使用.accelerate库优化推理性能 1. 引言 如果你正在使用Pixel Epic Wisdom Terminal进行AI推理任务&#xff0c;可能会遇到性能瓶颈问题。今天我们就来聊聊如何用Hugging Face的.accelerate库来提升推理速度&#xff0c;…...

H5扫码功能选型实战:微信JS-SDK vs 纯前端库,从公众号配置到代码封装的完整流程

H5扫码功能选型实战&#xff1a;微信JS-SDK vs 纯前端库的技术决策指南 当营销活动页需要实现"扫码领优惠券"功能时&#xff0c;技术团队突然陷入争论&#xff1a;是直接调用微信JS-SDK&#xff0c;还是采用纯前端扫码库&#xff1f;这个看似简单的技术决策&#xff…...

告别重复编码:用快马平台ai一键生成node.js效率工具脚本

最近在维护一个Node.js项目时&#xff0c;经常需要统计代码量。手动一个个文件查看实在太费时间&#xff0c;于是尝试用InsCode(快马)平台快速生成了一个代码统计工具&#xff0c;效果出乎意料地好。 需求分析 核心功能&#xff1a;需要递归扫描目录下的所有.js文件&#xff…...

大麦网自动购票工具:技术原理与多场景应用指南

大麦网自动购票工具&#xff1a;技术原理与多场景应用指南 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 在数字化票务时代&#xff0c;热门演出门票往往在开票瞬间售罄&…...