easyexcel上传校验的方法封装

easyexcel版本3.1.5

使用自定义注解的方式来定义校验的类型,避免冗余代码。

//校验value不能为空,且长度最大为30
@RowCheck(value = {RowCheckType.EMPTY,RowCheckType.LENGTH},max = 30)
private String value;

 具体代码

首先定义校验类型枚举RowCheckType:

package com.zhou.util.easyexcel;/*** 校验类型枚举* @author lang.zhou*/
public enum RowCheckType {EMPTY, //非空EMAIL, //邮件PATTERN, //正则LENGTH, //长度DATE, //日期ENUM //枚举
}

 定义校验不通过默认的提示语模板:

package com.zhou.zhou.util.easyexcel;/*** @author lang.zhou*/
public class CheckMessage {public static final String DEFAULT = "default";public static final String EMPTY = "第{n}行{c}不能为空";public static final String EMAIL = "第{n}行{c}邮箱格式错误";public static final String PATTERN = "第{n}行{c}格式错误";public static final String LENGTH = "第{n}行{c}长度超过{len}";public static final String DATE = "第{n}行{c}日期格式错误";public static final String ENUM = "第{n}行{c}不在枚举中";
}

定义校验的接口类ValueChecker :

package com.zhou.util.easyexcel.checker;import com.zhou.util.easyexcel.RowCheckType;/*** @author lang.zhou*/
public interface ValueChecker {/*** 实现校验逻辑*/boolean check(Object value);/*** 获得校验类型*/RowCheckType getType();/*** 获得默认校验提示*/String getDefaultMessage();
}

校验工厂类CheckFactory:

package com.zhou.util.easyexcel.checker;import com.zhou.util.easyexcel.RowCheck;
import com.zhou.util.easyexcel.RowCheckType;/*** @author lang.zhou*/
public class CheckerFactory {public static ValueChecker createChecker(RowCheckType type, RowCheck check){ValueChecker checker;if(type == RowCheckType.EMPTY){checker = new EmptyChecker();}else if(type == RowCheckType.EMAIL){checker = new EmailChecker();}else if(type == RowCheckType.PATTERN){checker = new PatternChecker(check.format());}else if(type == RowCheckType.LENGTH){checker = new LengthChecker(check.max());}else if(type == RowCheckType.DATE){checker = new DateChecker(check.format());}else if(type == RowCheckType.ENUM){checker = new EnumChecker(check.format());}else{checker = new EmptyChecker();}return checker;}
}

 封装的注解@RowCheck:

package com.zhou.util.easyexcel;import java.lang.annotation.*;/*** @author lang.zhou*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RowCheck {/*** 校验类型*/RowCheckType[] value() default {};String[] msg() default {CheckMessage.DEFAULT};/*** 正则校验/日期格式/枚举json*/String format() default "";/*** 最大长度校验*/int max() default 0;}

再定义一个easyexcel导入的映射实体类和excel模板如下:

package com.zhou.util.easyexcel.model;import com.alibaba.excel.annotation.ExcelProperty;
import com.zhou.util.easyexcel.RowCheck;
import com.zhou.util.easyexcel.RowCheckType;
import lombok.Data;import java.io.Serializable;/*** @author lang.zhou*/
@Data
public class ImportModel implements Serializable {private static final long serialVersionUID = 1L;@ExcelProperty(index = 0, value = "用户名")@RowCheck(value = {RowCheckType.EMPTY,RowCheckType.LENGTH},max = 30)private String username;@ExcelProperty(index = 1, value = "年龄")@RowCheck(value = RowCheckType.PATTERN,msg = "第{n}行年龄必须是整数")private String age;@ExcelProperty(index = 2, value = "邮箱")@RowCheck(value = {RowCheckType.EMPTY,RowCheckType.EMAIL,RowCheckType.LENGTH},msg = {CheckMessage.DEFAULT,CheckMessage.DEFAULT,CheckMessage.DEFAULT},max = 3000)private String email;}

实现校验的核心逻辑:

package com.zhou.util.easyexcel;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.zhou.util.easyexcel.checker.CheckerFactory;
import com.zhou.util.easyexcel.checker.EnumChecker;
import com.zhou.util.easyexcel.checker.ValueChecker;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;/*** @author lang.zhou*/
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class AbstractExcelReadListener<T> extends AnalysisEventListener<T> {/*** 每次读取数量*/protected int batchSize = 1000;/*** 数据总理*/protected int dataCount = 0;/*** 回调次数*/protected int times = 0;protected int sheetIndex = 0;protected Class<T> headerClass;/*** 数据读取开始行*/protected int headerLine = 1;protected Map<String, ValueChecker[]> checkTypeMap = new LinkedHashMap<>(0);protected Map<String,RowCheck> checkMap = new LinkedHashMap<>(0);protected Map<String,String> columnDesc = new LinkedHashMap<>(0);/*** 读取的数据缓存*/protected List<T> dataList = new ArrayList<>();public AbstractExcelReadListener(Class<T> headerClass) {this.headerClass = headerClass;}/*** 读取一行后回调* @param data  (列索引0开始 -> 值)*/@Overridepublic void invoke(T data, AnalysisContext context) {dataList.add(data);if(dataList.size() >= batchSize){callback();}}/*** 读取结束回调*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {callback();}/*** 读取头信息时初始化校验器*/@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {if(headerClass != null){Field[] fields = headerClass.getDeclaredFields();for (Field field : fields) {RowCheck check = field.getAnnotation(RowCheck.class);ExcelProperty ep = field.getAnnotation(ExcelProperty.class);if(check != null){RowCheckType[] checkType = check.value();if(checkType.length > 0){ValueChecker[] valueChecks = new ValueChecker[checkType.length];for (int i = 0; i < checkType.length; i++) {RowCheckType rowCheckType = checkType[i];//拿到校验器ValueChecker checker = CheckerFactory.createChecker(rowCheckType, check);valueChecks[i] = checker;}checkTypeMap.put(field.getName(),valueChecks);checkMap.put(field.getName(),check);}}if(ep != null){columnDesc.put(field.getName(),ep.value()[0]);}}}}private void callback(){if(dataList.size() > 0){dataCount += dataList.size();this.dataCallback(dataList, ++ times);dataList.clear();}}/*** 获取dataList中当前索引在全部批次数据中的行数* @param i     数据在当前批次dataList中的索引*/protected int getRowNum(int i){return (times - 1) * batchSize + i + 1;}/*** 读取batchSize行后回调*/public abstract void dataCallback(List<T> dataList,int times);public void read(InputStream stream){ExcelReader excelReader = EasyExcelFactory.read(stream, this).headRowNumber(headerLine).build();ReadSheet readSheet = EasyExcel.readSheet(sheetIndex).head(headerClass).build();excelReader.read(readSheet);excelReader.finish();}private String getMsg(ValueChecker checker, String[] msg, int i){String err = i < msg.length ? msg[i] : checker.getDefaultMessage();if(Objects.equals(err,CheckMessage.DEFAULT)){err = checker.getDefaultMessage();}return err;}/*** 校验一行数据(性能有待验证)* @param t         数据行* @param errMsg    存放校验错误信息* @return          true-校验通过*/@SneakyThrowsprotected boolean checkRow(T t, Set<String> errMsg){if(checkTypeMap.size() > 0){int dataIndex = dataList.indexOf(t);int rowNum = getRowNum(dataIndex);Field[] fields = headerClass.getDeclaredFields();for (Field field : fields) {RowCheck check = checkMap.get(field.getName());ValueChecker[] checkers = checkTypeMap.get(field.getName());if(checkers != null){String[] msg = check.msg();field.setAccessible(true);Object o = field.get(t);for (int i = 0; i < checkers.length; i++) {ValueChecker checker = checkers[i];if (!checker.check(o)) {String err = getMsg(checker,msg,i);if(StringUtils.isNotBlank(err)){err = err.replace("{n}",String.valueOf(rowNum)).replace("{v}",String.valueOf(o)).replace("{len}",String.valueOf(check.max())).replace("{c}",columnDesc.get(field.getName()));errMsg.add(err);return false;}}if(checker instanceof EnumChecker){Object enumKey = ((EnumChecker) checker).getEnumKey(o);field.set(t,enumKey);}}}}}return true;}}

其他的校验实现类:

@Data
public class EmptyChecker implements ValueChecker {private RowCheckType type = RowCheckType.EMPTY;private String defaultMessage = CheckMessage.EMPTY;@Overridepublic boolean check(Object value) {return !isEmpty(value);}public static boolean isEmpty(Object o){if(o==null){return true;}if(o instanceof String){return StringTool.isBlank(o.toString());}if(o instanceof Collection){return ((Collection<?>) o).isEmpty();}if(o instanceof Map){return ((Map<?,?>) o).isEmpty();}if(o.getClass().isArray()){return Array.getLength(o) == 0;}return false;}}@Data
public class DateChecker implements ValueChecker {private RowCheckType type = RowCheckType.DATE;private String defaultMessage = CheckMessage.DATE;private String format;public DateChecker(String format) {this.format = format;}@Overridepublic boolean check(Object value) {if(value != null){Date date = TimeUtil.formatDate(value.toString(), format);return date != null;}return true;}
}@Data
public class EmailChecker implements ValueChecker {public static final Pattern EMAIL_PATTERN = Pattern.compile("^\\w+([-\\\\.]\\w+)*@\\w+([-\\\\.]\\w+)*\\.\\w+([-\\\\.]\\w+)*$");private RowCheckType type = RowCheckType.EMAIL;private String defaultMessage = CheckMessage.EMAIL;@Overridepublic boolean check(Object value) {if(value != null){String str = value.toString();return StringUtils.isNotBlank(str) && EMAIL_PATTERN.matcher(str).matches();;}return true;}
}/*** @author lang.zhou*/
@Data
public class EnumChecker implements ValueChecker {private RowCheckType type = RowCheckType.ENUM;private String defaultMessage = CheckMessage.ENUM;private Map<String,?> json;public EnumChecker(String format) {try {this.json = reserveMap(JSON.parseObject(format));}catch (Exception e){//ignore}}private Map<String,Object> reserveMap(Map<String, String> enums){Map<String,Object> map = new HashMap<>(enums.size());for (Map.Entry<String, String> entry : enums.entrySet()) {map.put(entry.getValue(),entry.getKey());}return map;}@Overridepublic boolean check(Object value) {if(value != null){return getEnumKey(value) != null;}return true;}public Object getEnumKey(Object value) {if(value != null && json != null){return json.get(value.toString());}return null;}
}@Data
public class LengthChecker implements ValueChecker {private RowCheckType type = RowCheckType.LENGTH;private String defaultMessage = CheckMessage.LENGTH;private int max = 0;public LengthChecker(int max) {this.max = max;}@Overridepublic boolean check(Object value) {if(value != null){return value.toString().length() <= max;}return true;}
}@Data
public class PatternChecker implements ValueChecker {private RowCheckType type = RowCheckType.PATTERN;private String defaultMessage = CheckMessage.PATTERN;private Pattern pattern;public PatternChecker(String format) {pattern = Pattern.compile(format);}@Overridepublic boolean check(Object value) {if(value != null){return pattern.matcher(value.toString()).matches();}return true;}}

调用方式:

        //错误信息Set<String> err = new LinkedHashSet<>();AbstractExcelReadListener<ImportModel> listener = new AbstractExcelReadListener<ImportModel>(ImportModel.class) {@Overridepublic void dataCallback(List<ConsignorinfoImportModel> dataList, int times) {log.info("第{}次读取后保存",times);for (int i = 0; i < dataList.size(); i++) {ImportModel dto = dataList.get(i);Set<String> oneErr = new LinkedHashSet<>();try{//内置校验if(!this.checkRow(dto,oneErr)){continue;}//todo//这里可以进行自定义校验}finally {err.addAll(oneErr);}}}};listener.read(stream);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RTSP协议实现发送ACC音频数据

一.AAC音频格式介绍 AAC音频格式&#xff1a;Advanced Audio Coding&#xff08;高级音频解码&#xff09;&#xff0c;是一种由MPEG—4标准定义的有损音频压缩格式。音频压缩编码的输出码流&#xff0c;以音频帧的形式存在。每个音频帧包含若干个音频采样的压缩数据&#xff0…

Linux常用命令之cp、rm、touch、mv

cp: 复制文件或目录 -f 覆盖目标同名文件或目录时不进行提醒&#xff0c;而直接强制复制。-i 覆盖目标同名文件或目录时提醒用户确认。-p 复制时保持源文件的权限、属主及时间标记等属性不变&#xff08;默认权限属主是变化的&#xff09;。-r 复制目录时必须使用此选项&a…

vue3安装 router 路由

安装路由 cnpm i vue-router在src文件夹下创建router/index.ts import {createRouter,createWebHashHistory} from vue-router const routercreateRouter({history:createWebHashHistory(),routes:[{path:"/",name:home,component: () > import(../views/Home/i…

STL标准库与泛型编程(侯捷)笔记6(完结)

STL标准库与泛型编程&#xff08;侯捷&#xff09; 本文是学习笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 参考链接 Youbute: 侯捷-STL标准库与泛型编程 B站: 侯捷 - STL Github:STL源码剖析中源码 https://github.com/SilverMaple/STLSourceCo…

力扣(leetcode)第500题键盘行(Python)

500.键盘行 题目链接&#xff1a;500.键盘行 给你一个字符串数组 words &#xff0c;只返回可以使用在 美式键盘 同一行的字母打印出来的单词。键盘如下图所示。 美式键盘 中&#xff1a; 第一行由字符 “qwertyuiop” 组成。 第二行由字符 “asdfghjkl” 组成。 第三行由字…

从比特币、以太坊生态,到AI与新公链复兴,谁将接棒2024年的主流叙事?

2023年10月份至今&#xff0c;现货比特币ETF一直都是促使市场反弹的核心叙事之一&#xff0c;如今靴子终于落地&#xff0c;那在ETF预期尘埃落定的大背景下&#xff0c;接下来的加密市场有哪些赛道值得关注&#xff1f; 泛比特币生态 2023年比特币生态浪潮中&#xff0c;OKX等赢…

企业信息化规划该如何落地?以制造型企业为例

企业信息化规划如何落地&#xff1f; 规划做好了&#xff0c;蓝图也画好了&#xff0c;人手一块大饼也已经揣好了&#xff0c;那么该怎么落地呢&#xff0c;这才是最关键的。 我将企业信息化规划落地分为4个周期&#xff0c;以最典型的制造行业为例&#xff0c;以简道云这个企…

Seata TM管理分支事务源码

TM相当于一个中间商&#xff0c;是没有涉及到任何数据库底层操作的。 TransactionalTemplate 1、TM向TC端发起一次开启全局事务的请求 io.seata.tm.api.TransactionalTemplate#beginTransaction --> io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.Strin…

配置DNS

vim /etc/named.conf vim /etc/named.rfc1912.zones cp named.localhost ./kgc.com.zone -p vim kgc.com.zone 设置备用dns服务器 修改主配置文件&#xff0c;并自动同步到从服务器

【分布式技术】监控平台zabbix介绍与部署

目录 一、为什么要做监控&#xff1f; 二、zabbix是什么&#xff1f; 三、zabbix有哪些组件&#xff1f; ​编辑Zabbix 6.0 功能组件&#xff1a; ●Zabbix Server ●数据库 ●Web 界面 ●Zabbix Agent ●Zabbix Proxy ●Java Gateway 四、zabbix的工作原理&#xf…

SQL-数据类型

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

【python】进阶--->MySQL数据库(二)

一、sql语句(结构化查询语言) 要和数据库进行交互,需要使用到数据库认识的语言 : sql语句 是关系型数据库都需要遵循的规范。不同数据库都支持sql语句,但是都有特有内容。 二、sql语句分类 数据定义语言 : 用来定义数据库–数据库,表,列. 数据操作语言 : 对数据库表中的记录进…

Ubuntu12.0安装g++过程及其报错

Ubuntu12.0安装g过程及其报错 https://blog.csdn.net/weixin_51286763/article/details/120703953 https://blog.csdn.net/dingd1234/article/details/124029945

MATLAB | 龙年大吉,使用MATLAB绘制会动的中国风神龙

hey各位好久不见&#xff0c;龙年到了&#xff0c;这期画一期配色非常中国风的龙&#xff0c;这个造型的龙参考了某些html绘制龙的视频&#xff0c;但是由于html版全网都是也不咋给代码和代码出处&#xff0c;因此自己写了个MATLAB版本&#xff1a; 可以看到还是非常酷炫的&…

【LabVIEW FPGA入门】使用数字IO卡实现计数器输入功能

方法1&#xff1a; 1.首先需要用一个数字IO的输入FPGA端口&#xff0c;并将其拖入程序框图中&#xff0c;同时创建一个循环。 2.如果想要在循环中实现累加功能&#xff0c;就可以使用移位寄存器。 数字输入的当前值和历史值进行比较&#xff0c;用于一个判断大于&#xff0c;来…

【算法分析与设计】跳跃游戏

题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - …

阿里云高性能云服务器_云主机_云服务器详解

阿里云高性能云服务器60%单实例最大性能提升&#xff0c;35Gbps内网带宽&#xff0c;网络增强&通用型云服务器、本地SSD型云服务器、大数据型云服务器、GPU异构型云服务器&#xff0c;阿里云百科aliyunbaike.com分享阿里云高性能云服务器&#xff1a; 阿里云高性能云服务器…

基于STM32的温湿度传感器

一、创新实践实习内容 一:指导老师给我们介绍了广州粤嵌通信科技股份有限公司的企业文化与企业环境&#xff0c;简要地介绍了本行业的发展历史及未来发展趋势。讲解了Keil5的功能与应用。指导我们搭建STM32F407驱动环境以及学习相关芯片包的安装使用方法。并且带着我们对C语言…

力扣热题100

排序 快速排序 #include <iostream> #include <vector> using namespace std;// 快速排序函数&#xff0c;传入引用&#xff0c;以便修改原始数组 void quick_sort(vector<int>& q, int l, int r) {// 边界条件&#xff1a;如果左边界大于等于右边界&am…

基于SpringBoot+Vue实现的二手交易系统

系统介绍 校园二手交易网站是一种专门针对有二手物品交易需求用户的二手交易的网站。它的设计和开发主要是为了满足用户之间的二手物品交易需求&#xff0c;方便大家在线买卖二手物品。近年来&#xff0c;随着互联网技术的发展&#xff0c;人们越来越喜欢在线购物&#xff0c;…