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开始 -> 值)
     */
    @Override
    public void invoke(T data, AnalysisContext context) {
        dataList.add(data);
        if(dataList.size() >= batchSize){
            callback();
        }
    }

    /**
     * 读取结束回调
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        callback();
    }

    /**
     * 读取头信息时初始化校验器
     */
    @Override
    public 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-校验通过
     */
    @SneakyThrows
    protected 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;

    @Override
    public 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;
    }

    @Override
    public 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;

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    public 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) {
            @Override
            public 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);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习目标检测】十四、基于深度学习的血细胞计数系统-含GUI(BCD数据集,yolov8)

血细胞计数是医学上一种重要的检测手段&#xff0c;用于评估患者的健康状况&#xff0c;诊断疾病&#xff0c;以及监测治疗效果。而目标检测是一种计算机视觉技术&#xff0c;用于在图像中识别和定位特定的目标。在血细胞计数中&#xff0c;目标检测技术可以发挥重要作用。 首先…

python统计分析——小提琴图(sns.violinplot)

参考资料&#xff1a;用python动手学统计学&#xff0c;帮助文档 使用seaborn.violinplot()函数绘制箱线图 sns.violinplot()的做出来的小提琴图比plt.violinplot()更像小提琴。 import numpy as np import pandas as pd from matplotlib import pyplot as plt import seabo…

智能小程序能做什么?

一. 自定义Tab页 涂鸦提供了丰富的场景化、个性化的 ToC 智能服务&#xff0c;不仅可以快速低成本的自由搭建出更多智能服务&#xff0c;还为你提供了基于小程序技术方案的可自主可控的自定义开发链路&#xff0c;为拓展更多品牌化、个性化、差异化智能服务提供生态基础。 我…

【Matlab】在Matlab中安装优化工具yalmip的方法

最近博主想做一些关于多目标优化的问题&#xff0c;因为之前对Matlab有一定经验&#xff0c;所以直接在网上查找了如何在Matlab上实现多目标优化的文献&#xff0c;看到有人提到了yamip&#xff0c;于是博主就试着在Matlab中安装yamip&#xff0c;将其中遇到的问题和一些经验和…

【Oracle】Oracle编程PLSQL

Oracle编程 一、PL/SQL 1、PL/SQL概述 PL/SQL&#xff08;Procedure Language/SQL&#xff09;是 Oracle 对 sql 语言的过程化扩展&#xff0c;使 SQL 语言具有过程处理能力。 基本语法结构 [declare -- 声明变量 ]begin-- 代码逻辑 [exception-- 异常处理 ]end;2、变量 …

004 Golang-channel-practice 左右括号匹配

第四题 左右括号打印 一个协程负责打印“&#xff08;”&#xff0c;一个协程负责打印“&#xff09;”&#xff0c;左右括号的数量要匹配。在这道题目里&#xff0c;我在main函数里进行了一个死循环。会产生一个随机数&#xff0c;随机数就是接下来要打印的左括号的数量。 例…

C#编程-了解线程的优先级

了解线程的优先级 控制线程行为的一个属性是它的优先级。.NET运行时环境基于它们的优先级执行线程。CPU一次仅执行一个线程。因此,处于执行的可运行状态的线程,排队等待轮到被处理器执行。线程是固定优先级调度的。带有优先级的每个线程在处理器的线程队列中有自己的位置。 …

Nginx多虚拟主机配置

最近写公司项目&#xff0c;需要和前端小伙伴对接&#xff0c;但是有时候只是后端的一个bug&#xff0c;也不好意思一直让前端小伙伴帮忙起服务&#xff0c;所以想着直接拿测试包在本地起一个前端环境&#xff0c;这样后续开发比较方便&#xff1b;因为公司项目有好几个&#x…

22款奔驰C260L升级ACC自适应巡航 解放双脚 出行更加安全

有的时候你是否厌倦了不停的刹车、加油&#xff1f;是不是讨厌急刹车&#xff0c;为掌握不好车距而烦恼&#xff1f; 如果是这样&#xff0c;那么就升级奔驰原厂ACC自适应式巡航控制系统&#xff0c;带排队自动辅助和行车距离警报功能&#xff0c;感受现代科技带给你的舒适安全…

<软考高项备考>《论文专题 - 63 质量管理(2) 》

2 过程1-规划质量管理 2.1 问题 4W1H过程做什么识别项目及其可交付成果的质量要求、标准&#xff0c;并书面描述项目将如何证明符合质量要求、标准的过程&#xff1b;作用&#xff1a;为在整个项目期间如何管理和核实质量提供指南和方向为什么做1、识别项目/产品质量要求和标…

Github项目推荐-Insomnia

项目地址 GitHub地址&#xff1a;GitHub - Kong/insomnia 官网&#xff1a;The Collaborative API Development Platform - Insomnia 项目简述 想必大家都知道PostMan吧。Insomnia可以说是PostMan的开源平替。页面ui很不错&#xff0c;功能强大&#xff0c;使用也比较方便。…

【ChatGPT-Share,国内可用】GPTS商店大更新:一探前沿科技的魅力!

使用地址&#xff1a;https://hello.zhangsan.cloud/list GPTS商店预览,王炸更新 精选应用&#xff1a; 系统内置应用&#xff1a; 绘画应用&#xff1a; 写作应用&#xff1a; 高效工具应用&#xff1a; 学术搜索和分析应用&#xff1a; 编程应用&#xff1a; 教育应…

开源项目CuteSqlite开发笔记(七):CuteSqlite释放BETA版本啦

经过大半年的开发&#xff0c;CuteSqlite程序代码不知不觉来到了6万行&#xff0c;有效行数4万行&#xff0c;CuteSqlite开发完成了一个小版本&#xff0c;进入下一个阶段&#xff0c;并于2024元旦释放BETA版本&#xff0c;有兴趣的朋友可以下载试用。 GitHub下载https://gith…

基于Docker Compose单机实现多级缓存架构2024

文章目录 一、环境参考二、专栏简介三、扩展 一、环境参考 NameVersionDocker Desktop for Windows4.23.0Openjdk8MySQL8.2.0Redis7.2Canal1.1.7OpenResty1.21.4.3-3-jammy-amd64Lua-Caffeine- 二、专栏简介 多级缓存实现过程比较长&#xff0c;将拆分为多个文章分步讲述。如…

【算法】信使(最短路问题)

题目 战争时期&#xff0c;前线有 n 个哨所&#xff0c;每个哨所可能会与其他若干个哨所之间有通信联系。 信使负责在哨所之间传递信息&#xff0c;当然&#xff0c;这是要花费一定时间的&#xff08;以天为单位&#xff09;。 指挥部设在第一个哨所。 当指挥部下达一个命令…

低维度向量的 Householder 反射变换 matlab 图示

1, 算法原理 设th 是一个弧度值&#xff0c; 令 Q | cos(th) sin(th) | | sin(th) -cos(th) | S span{ | cos(th/2.0) | } | sin(th/2.0) | x (x1, x2) 是一个平面上的二维向量 计算 y Qx Qx 则&#xff0c;y 是 x 通过有 S 定…

基于STM32F103和ESP8266的Wi-Fi模块驱动程序设计与优化

基于STM32F103和ESP8266的Wi-Fi模块驱动程序设计和优化是一个重要的任务&#xff0c;它将使STM32F103微控制器能够与ESP8266模块进行通信并实现无线网络连接。在本文中&#xff0c;我们将介绍如何设计和优化这样的驱动程序&#xff0c;并提供相关的代码示例。 1. 系统概述 Wi…

Ubuntu root 远程登录失败

背景&#xff1a;设置了两个系统用户&#xff1a;root、test&#xff1b;test可以登录&#xff0c;可以使用su 命令切换root用户登录成功&#xff1b; 但是直接用root登录&#xff0c;会报错。 查看登录日志的方法&#xff1a; 需要两个远程窗口&#xff0c;在第一个远程窗口…

案例103:基于微信小程序的移动网赚项目设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder …

OCR字符识别:开始批量识别身份证信息

身份证信息批量识别OCR是一项解决方案&#xff0c;它能够将身份证照片打包成zip格式或通过URL地址进行提交&#xff0c;并能够识别照片中的文本信息。最终&#xff0c;用户可以将识别结果生成为excel文件进行下载。 API接口功能&#xff1a; 1. 批量识别&#xff1a;支持将多…