SpringBoot整合EasyExcel实现读操作

news2024/11/24 20:39:00
😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: SpringBoot整合EasyExcel实现读操作
⏱️ @ 创作时间: 2023年07月18日

在这里插入图片描述

目录

      • 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
    @Resource
    private ReadExcelService excelService;

    //存放解析后的数据
    private List<PersonData> list = new ArrayList<>();

    /**
     * 读取每一条头部信息
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
    }


    /**
     * 这个每一条数据解析都会来调用
     *
     * @param context
     */
    @Override
    public void invoke(PersonData data, AnalysisContext context) {
    	//调用业务类,对数据进行校验
	    excelService.checkPersonExcel(data, rowIndex);
        list.add(data);
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public 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 ,方便内存回收
     */
    @Resource
    private 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();
         //指定读取的Sheet
         ReadSheet 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> {
    @Override
    public Class supportJavaTypeKey() {
        return Double.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.NUMBER;
    }

    /**
     * 这里读的时候会调用
     *
     * @param cellData            NotNull
     * @param contentProperty     Nullable
     * @param globalConfiguration NotNull
     * @return
     */
    @Override
    public Double convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) {
        CellDataTypeEnum type = cellData.getType();
        double newValue = 0.00d;
        if (type == CellDataTypeEnum.STRING) {
            //如果为文本则转换为为Double
            newValue = Double.valueOf(cellData.getStringValue());
        } else {
            newValue = Double.valueOf(cellData.getNumberValue().toString());
        }
        return newValue;
    }

    /**
     * 这里是写的时候会调用
     *
     * @param value               NotNull
     * @param contentProperty     Nullable
     * @param globalConfiguration NotNull
     * @return
     */
    @Override
    public 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> {

    @Resource
    private ReadExcelService excelService;

    //存放解析后的数据
    private List<T> list = new ArrayList<>();

    /**
     * 读取每一条头部信息
     *
     * @param headMap
     * @param context
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public 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

在这里插入图片描述

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

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

相关文章

linux中的sendmail发送邮件

Linux/UNIX 下的老牌邮件服务器。 Sendmail 作为一种免费的邮件服务器软件&#xff0c;已被广泛的应用于各种服务器中&#xff0c;它在稳定性、可移植性、及确保没有 bug 等方面具有一定的特色&#xff0c;且可以在网络中搜索到大量的使用资料。 一、邮件发送原理图 MUA&#x…

通过弹性算力支持与托管式机器学习服务,亚马逊云科技为客户提升技术竞争力

时下数字化浪潮中&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;已成为企业实现业务增长和数字化转型的重要技术&#xff0c;为企业提供了重新定义和调优业务模式的机遇。越来越多的企业希望利用人工智能技术提升竞争力&#xff0c;应对复杂的商业环境和市场挑战&…

机器学习实战:Python基于Ridge岭回归进行正则化(十三)

文章目录 1.前言1.1 岭回归的介绍1.2 岭回归的应用 2.自定义数据集实战演示2.1 导入函数2.2 创建数据集2.3 alpha0、1、10、100的分别情况 3.Dushanbe_house数据集实战演示3.1 导入函数和数据3.2 剔除空值及可视化3.3 整理数据3.4 训练和测试数据集3.5 评估数据集 4.讨论 1.前言…

mysql更新关联字段问题

现象 ### 表结构 CREATE TABLE wjf_test_update_num (id bigint(20) NOT NULL AUTO_INCREMENT,num1 int(11) DEFAULT NULL,num2 int(11) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT3 DEFAULT CHARSETutf8 |## 插入两行数据 insert into wjf_test_update_nu…

从源码理解Scala中函数reduceRight的计算过程

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 以List集合为例&#xff0c;进行reduceRight()的计算过程分析&#xff0c;总体分为两部分&#xff0c;一部分是看最顶层特质的那个通用的reduceRight方法&#xff0c;另一部分是讲直接混入的特质的…

【Linux】分布式存储系统 Ceph

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 分布式存储系统 Ceph Ceph 概述1、Ceph 简介2、存储基础3、Ceph 优势4、Ceph 架构4、Ceph 核心组件5、OSD 存储后端6、Ceph 数据的存储过程7、Ceph 版本发行生命周期 Ceph 集…

Day13 02-Linux常用命令汇总

文章目录 第三章 Linux的常用命令【重要】3.1 命令格式的说明3.2 帮助命令3.2.1 man3.2.2 help 3.3 文件处理命令3.3.1 cd3.3.2 ls3.3.3 pwd3.3.4 mkdir3.3.5 touch3.3.6 echo3.3.7 cp3.3.8 mv3.3.9 rm3.3.10 vi编辑器3.3.11 ln 3.4 查看命令3.4.1 cat3.4.2 more3.4.3 head3.4.…

代码量、代码行数计算工具

cloc 极客命令行工具 下载地址&#xff1a;https://github.com/AlDanial/cloc 常用命令&#xff1a; // 文件夹的名称 cloc ./StaticAnalyzer/计算结果&#xff1a; 速度快&#xff0c;结果精准&#xff0c;十分实用&#xff01;

51单片机学习--数码管显示

首先实现静态数码管显示 SMG表示共阴极 &#xff0c;要让第三位显示数字6&#xff0c;需要LED6端口接0&#xff0c;即Y50&#xff0c;5的二进制为101&#xff0c;按P2_4 到 P2_2 的顺序接&#xff0c;则CBA分别接101 然后在数码管另一端&#xff0c;需要点亮的接上1&#xff…

亚信科技荣任「DBL电信行业工作组」副组长单位,AntDB数据库连年入选《中国数据库产品图谱》

日前&#xff0c;“2023可信数据库发展大会”在京圆满召开。亚信科技凭借自研的电信级核心交易数据库AntDB在通信行业15年的技术积累和行业贡献&#xff0c;成功当选为数据库应用创新实验室&#xff08;DBL&#xff09;电信行业工作组副组长单位。AntDB数据库连续两年入选《全球…

客户案例 | 数字化加速,金融企业实现3D打印式应用程序开发

关键发现&#xff1a; 客户痛点&#xff1a;传统开发周期长&#xff0c;流程复杂&#xff0c;难以满足杭银消金在企业快速发展过程中的应用开发需求&#xff1b;内部业务因为优先级不高&#xff0c;导致开发资源分配有限&#xff0c;更加迟滞了管理部门数字化转型的进度。 解决…

大规模新能源并网下火电机组深度调峰优化调度

1主要内容 程序主要参考《大规模新能源并网下火电机组深度调峰优化调度》&#xff0c;建立了深度调峰基础模型&#xff0c;采用IEEE30节点系统&#xff0c;通过直流潮流建模&#xff0c;以火电机组运行成本、开机成本等综合运行成本最低为目标函数&#xff0c;包括潮流约束、旋…

Spring 能解决所有循环依赖吗?

以下内容基于 Spring6.0.4。 看了上篇文章的小伙伴&#xff0c;对于 Spring 解决循环依赖的思路应该有一个大致了解了&#xff0c;今天我们再来看一看&#xff0c;按照上篇文章介绍的思路&#xff0c;有哪些循环依赖 Spring 处理不了。 严格来说&#xff0c;其实也不是解决不了…

基于Python的汉字字频统计实验

完整资料进入【数字空间】查看——baidu搜索"writebug" 实验内容 针对不同语料统计汉字的字频&#xff0c;并进行比较。 实验要求和目的 给出前 100 个汉字高频字的频率统计结果&#xff1b; 分别给出前 1、20、100、600、2000、3000、6000 汉字的字频总和&…

makefile项目管理-规则和过程

目录 1、makefile基本规则 2、makefile执行过程 3、makefile的运行规则 1、makefile基本规则 &#xff08;1&#xff09;命名&#xff1a;makefile 和 Makefile &#xff08;2&#xff09;makefile文件&#xff1a;里面是多个命令的集合&#xff0c;使用make命令执行该文件 …

岩土工程安全监测振弦传感器的发展史

岩土工程安全监测振弦传感器的发展史 岩土工程安全监测是岩土工程学科领域的一个重要组成部分。随着科学技术的不断发展&#xff0c;传感器技术的应用也在不断地创新和完善。振弦传感器是一种重要的监测设备&#xff0c;其应用范围广泛&#xff0c;以其高灵敏度、高精度、长期…

Python实现HBA混合蝙蝠智能算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝙蝠算法是2010年杨教授基于群体智能提出的启发式搜索算法&#xff0c;是一种搜索全局最优解的有效方法…

EPSG代号-坐标系对应表

前言 EPSG 用于标识不同的地理空间参考系统&#xff0c;包括坐标系统、地理坐标系、投影坐标系等。这些标识符可用于许多应用程序和地理信息系统软件&#xff0c;以确保数据在不同系统之间的正确转换和处理。现在&#xff0c;EPSG已被Open Geospatial Consortium&#xff08;OG…

Js: 读取数据并动态生成表格(读取新数据时,应该删除之前已经渲染出来的数据)

前言 使用JS读取数据并动态生成表格&#xff0c;但是发现在读取新一轮的数据时&#xff0c;新数据是在之前已经渲染的数据后面进行追加。因此需要解决的问题是&#xff1a;在读取新数据之前&#xff0c;把之前已经渲染的数据进行清空。 解决 1、首先写出表格的表头和主干 &…

rsync增量原理,及C++实现

1、目标端将目标文件按700字节为大小分块计算强弱校验值&#xff08;强&#xff1a;md5 弱&#xff1a;adler32&#xff0c;注&#xff1a;弱校验码计算量小速度快&#xff0c;先比对弱校验码&#xff0c;弱校验值一样再比对强校验码&#xff09;&#xff0c;再结合块号组成一…