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

news2024/10/1 12:22:42

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,当前行等数据
     */
    @Override
    public 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);
        }

        //根据业务自行 dosomething
        doSomething();
    }

    /**
     * 根据业务自行实现该方法
     */
    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
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (head) {
            errDatas.removeIf(a -> !a.getLine().equals("1"));
            datas.clear();
        }
        /*
            datas.clear();
            解析结束销毁不用的资源
         */
    }

	 /**
     * 解析出错时的操作
     *
     * @param object
     */
    @Override
    public 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);
        }
    }

    @Override
    public 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;
        //循环遍历所有的fields
        for (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索引是上一行索引的,如果有,则索引+1
            if (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相关,感兴趣可以看看

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

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

相关文章

《蓝桥杯算法入门》(C/C++、Java、Python三个版本)24年10月出版

推荐&#xff1a;《算法竞赛》&#xff0c;算法竞赛大全书&#xff0c;网购&#xff1a;京东 天猫  当当 文章目录 《蓝桥杯算法入门》内容简介本书读者对象作者简介联系与交流《蓝桥杯算法入门 C/C》版目录 《蓝桥杯算法入门 Java》版目录 《蓝桥杯算法入门 Python》版目录 …

vscode 连接服务器 不用输密码 免密登录

vscode 免密登录服务器 1. 本地端1&#xff09;生成密钥2&#xff09;vscode的config文件中添加id_rsa文件路径 2. 服务器端1&#xff09;在 authorized_keys 文件中加入密钥&#xff1a;2&#xff09;修改authorized_keys文件权限 1. 本地端 1&#xff09;生成密钥 在命令行…

【hot100-java】【二叉树的层序遍历】

二叉树 BFS 队列实现 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right)…

力扣(leetcode)每日一题 1845 座位预约管理系统| treeSet和priority Queue的区别|线段树

之前发过一篇&#xff0c;感觉还有深挖的地方&#xff0c;于是又补充一些信息 这题目虽然是middle难度题目&#xff0c;要解答出来是只要easy的时间&#xff0c;但是深挖可以有hard的难度 题解1 可以帮助复习线段树的使用&#xff0c;题解2 可以复习一下java基础知识 题解1 线…

免费送源码:Java+ssm+MySQL springboot健康医疗系统 计算机毕业设计原创定制

摘 要 随着我国经济迅速发展&#xff0c;人们对医疗管理的需求越来越大&#xff0c;各种健康医疗系统也都在被广泛应用&#xff0c;对于医疗管理的各种软件也是备受用户的喜爱&#xff0c;健康医疗系统被用户普遍使用&#xff0c;为方便用户能够可以随时进行健康医疗系统的数据…

【JAVA开源】基于Vue和SpringBoot的美容院管理系统

本文项目编号 T 055 &#xff0c;文末自助获取源码 \color{red}{T055&#xff0c;文末自助获取源码} T055&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

NAL 网络提取层(Network Abstraction Layer)

1.NAL全称Network Abstract Layer, 即网络抽象层。 在H.264/AVC视频编码标准中&#xff0c;无论是存储还是网络传输&#xff0c;H264 原始码流是由一个接一个 NALU&#xff08;NAL Unit&#xff09; 组成&#xff0c;整个系统框架被分为两个层面&#xff1a;视频编码层面&#…

uniapp学习(003-1 vue3学习 Part.1)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第11p-第p14的内容 文章目录 vue3使用介绍插值表达式例子时间戳随机数输出函数的值 ref响应式数据变量v-bind 绑…

PCL CropBox 过滤给定立方体内的点云数据

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 点云裁剪 2.1.2 可视化原始点云和裁剪后的点云 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长…

Python办公自动化案例:批量修改Word文件中的段落格式

案例:Python实现批量修改Word文件中的段落格式。 在处理大量Word文档时,经常需要批量修改这些文档的格式,比如统一段落格式,以提升文档的一致性和专业性。使用Python来实现这一任务可以极大地提高工作效率,特别是当涉及到数百或数千个文档时。Python通过第三方库如python…

【C#】CacheManager:高效的 .NET 缓存管理库

在现代应用开发中&#xff0c;缓存是提升性能和降低数据库负载的重要技术手段。无论是 Web 应用、桌面应用还是移动应用&#xff0c;缓存都能够帮助减少重复的数据查询和处理&#xff0c;从而提高系统的响应速度。然而&#xff0c;管理缓存并不简单&#xff0c;尤其是当你需要处…

《RabbitMQ篇》Centos7安装RabbitMQ

安装RabbitMQ 安装包网盘下载地址 链接&#xff1a;https://pan.baidu.com/s/1bG_nP0iCdAejkctFp1QztQ?pwd4mlw 先上传安装包到服务器&#xff08;erlang-23.3.4.11-1.el7.x86_64.rpm和rabbitmq-server-3.9.16-1.el7.noarch.rpm&#xff09;然后使用指令安装 # 安装 erlang r…

掌握 JVM 垃圾收集线程:简化 VM 选项

垃圾收集阶段对于任何 Java 应用程序都至关重要。主要目标是保持高吞吐量和低延迟之间的平衡。通过配置垃圾收集器&#xff0c;我们可以提高性能&#xff0c;或者至少推动应用程序朝着特定的方向发展。 垃圾收集周期越短越好。因此&#xff0c;分配给垃圾收集器的资源越多&…

昇思MindSpore进阶教程--下沉模式

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 正文开始 昇腾芯片集成了AICORE和AICPU等…

C#自定义工具类-数组工具类

目录 数组工具类基本操作 1.排序&#xff1a;升序&#xff0c;降序 2.查找 1&#xff09;查找最值&#xff1a;最大值&#xff0c;最小值 2&#xff09;查找满足条件的单个对象 3&#xff09;查找满足条件的所有对象 4&#xff09;选取数组中所有对象的某一字段 完整代…

河南做网站与SEO:如何提升搜索引擎排名

河南做网站与SEO&#xff1a;如何提升搜索引擎排名 在当今数字化时代&#xff0c;越来越多的企业意识到互联网的重要性&#xff0c;特别是在河南这样一个快速发展的地区&#xff0c;建立一个优秀的网站已经成为企业发展的必要条件。而在建立网站的同时&#xff0c;SEO&#xff…

--- java数据结构 map set ---

java中map 和 set的底层实现是通过搜索树和哈希函桶来实现 搜索树 二叉搜索树有叫二叉排序树 他具有以下的特点 若存在左节点&#xff0c;那么他左节点的值一定小于根节点 若存在右节点&#xff0c;那么他右节点的值一定大于根节点 它的左右子树也是搜索树 对他进行中序…

leetcode热题100.最长公共子序列

题目 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原…

SOMEIP_ETS_143: SD_Request_non_existing_ServiceID

测试目的&#xff1a; 验证DUT能够拒绝一个请求不存在的服务ID&#xff08;ServiceID&#xff09;的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议&#xff0c;当接收到一个请求不存在服务ID的Subs…

RS485串口通信:【图文详讲】

RS485&#xff0c;RS的意义为Recommended Standard的缩写&#xff0c;也就是推荐标准&#xff0c;是一种常用的半双工-异步-串行通信总线。半双工的意思就是两者通信时&#xff0c;同一时刻&#xff0c;只能由其中一方发送&#xff0c;另一方只能接收&#xff0c;不可以同时收发…