SpringBoot + EasyExcel 实现表格数据导入

news2024/12/29 23:12:21

1. 准备

导入依赖

<dependency>
	<groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
    <scope>compile</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>1.3.1</version>
</dependency>

本次示例使用的表格导入模板

在这里插入图片描述

2. 后端

2.1 config

2.1.1 自定义异常

抛出该异常后停止解析

public class ExcelAnalysisStopException extends ExcelAnalysisException {

    public ExcelAnalysisStopException(String message) {
        super(message);
    }
    
}

数据转换异常错误

@Getter
@Setter
public class ExcelDataConvertException extends RuntimeException {

    private Integer rowIndex;
    private Integer columnIndex;

    private CellData cellData;
    private ExcelContentProperty excelContentProperty;

    public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData) {
        super("第" + rowIndex + "行第" + columnIndex + "列,数据:" + cellData + "发生数据类型转换错误");
    }

}

2.1.2 监听器

在监听接口中处理异常

public abstract class AnalysisEventListener<T> implements ReadListener<T> {
    public AnalysisEventListener() {
    }

    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
    }

    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    }

    public void extra(CellExtra extra, AnalysisContext context) {
    }

    public void onException(Exception exception, AnalysisContext context) throws Exception {
        throw exception;
    }  //默认抛出异常

    public boolean hasNext(AnalysisContext context) {
        return true;
    }
}

核心监听器类

@Data
public class ExcelListener extends AnalysisEventListener {
	//  在下面进行补充
	//  表格数据的解析和导入都是在这里进行
	//  要注意的是,该监听器不支持交给 SpringBoot 管理,所以要将需要的类 new 出来,然后传进该监听器中
}

2.2 表格数据对应的实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Departments implements Serializable {

	//  @ExcelIgnore 表示在进行表格数据导入解析时,忽略该属性
    @ExcelIgnore
    private Integer id;

    @ExcelProperty("院系名称")
    private String departmentName;

    private static final long serialVersionUID = 1L;
}

2.2 controller

@RestController
@RequestMapping("/excel")
@AllArgsConstructor
public class ExcelImportController {

    private ExcelImportService excelImportService;

	@PostMapping("/importData")
    public String importData(@RequestPart("file") MultipartFile file) {
        if (file == null || flag == null) {
            throw new Exception("文件为空!");
        }
        return excelImportService.importData(file);
    }
}

2.3 service

/**
 * 表格导入 service 接口
 */
public interface ExcelImportService {
    String importData(MultipartFile file);
}

表格导入的前提,是要有一个规定的表头模板
在解析时,我们要根据规定的表头去解析。如果提供的表格表头不正确直接抛出异常给前端

@Service
@AllArgsConstructor
public class ExcelImportServiceImpl implements ExcelImportService {
    private final DepartmentsMapper departmentMapper;

    @Override
    public String importData(MultipartFile file) {
        String[] headList = {"院系名称"};  //  表头
        try {
            EasyExcel.read(file.getInputStream(), Departments.class, new ExcelListener(headList, departmentMapper)).sheet().doRead();
        } catch (Exception e) {
            return "导入失败";
        }
        return "导入成功";
    }
}

2.4 监听器逻辑补充

@Data
public class ExcelListener extends AnalysisEventListener {
	
    private Integer num;  //  Excel行数
    private String message;  //  校验规则信息
    private String[] headList;  //  表头模板数据
    private static final int BATCH_COUNT = 30; //  每隔30条存数据库,然后清理list ,方便内存回收
    private List datas = new ArrayList<>();  //  数据集合
    private DepartmentsMapper departmentMapper;

	public ExcelListener(String[] headList, DepartmentsMapper departmentMapper) {
        this.num = 0;
        this.message = "";
        this.headList = headList;
        this.departmentMapper = departmentMapper;
    }

	/**
     * 读取表头
     * @param headMap 表头Map
     * @param context
     */
    @Override
    public void invokeHeadMap(Map headMap, AnalysisContext context) {
        if (context.readRowHolder().getRowIndex() == 0) {
            for(int i = 0; i < headList.length; i++) {
                try {
                    if (!headMap.get(i).equals(headList[i])) {
                        datas.clear();
                        message = "上传模板与系统模板不匹配";
                        throw new ExcelAnalysisStopException(message);
                    }
                } catch (Exception e) {
                    datas.clear();
                    message = "上传模板与系统模板不匹配";
                    throw new ExcelAnalysisStopException(message);
                }
            }
        }
    }

	/**
     * 每一条数据解析都会来调用
     * @param object 当前行的数据对象
     * @param analysisContext
     */
    @Override
    public void invoke(Object object, AnalysisContext analysisContext) {
        if (isNull(object)) {
            datas.add(object);  //  数据存储到list,供批量处理,或后续自己业务逻辑处理
            num++;
        }

        if (datas.size() >= BATCH_COUNT) {
            saveData();
            datas.clear();  //  清理存储在内存中的数据
        }
    }

	/**
	 * 本人自己定义的方法
     * 判断导入表格中的数据是否有为空值
     * @param object 要进行进行检查的对象
     * @return 不为空则为true,反之
     */
    private boolean isNull(Object object) {
        boolean isNull = true;

        //  自己业务的逻辑

        return isNull;
    }

	/**
     * 所有数据解析完成都会来调用
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (num == 0) {
            datas.clear();
            message = "上传的表格数据为空或上传的表格数据无效";
            throw new ExcelAnalysisStopException(message);
        }
        saveData();  //  自定义的保存数据方法
        datas.clear();
    }

	/**
     * 在转换异常 获取其他异常下会调用本接口,抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行
     * @param exception 出现的异常
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        datas.clear();

        ExcelDataConvertException excelDataConvertException = null;
        if (exception instanceof ExcelDataConvertException) {
            excelDataConvertException = (ExcelDataConvertException) exception;
        }
        if (null != excelDataConvertException) {
            int row = excelDataConvertException.getRowIndex() + 1;
            int col = excelDataConvertException.getColumnIndex() + 1;
            message = "第" + row + "行,第" + col + "列," +  "解析异常";
        }

        throw new ExcelAnalysisStopException(message);
    }

	/**
     * 当出现模板数据异常时,结束往下解析,抛出异常
     * @param context
     * @return 布尔值
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        return true;
    }

	/**
     * 插入数据到数据库
     * @return 受影响的行数
     */
	private int saveData() {
        removeDuplicate(datas);  //  去重
        return departmentMapper.insertBatch(datas);
    }

	/**
     * 移除List中重复的元素
     * @param list 原List集合
     * @return 去重后的List集合
     */
    private static List removeDuplicate(List list) {
        Set h = new HashSet(list);
        list.clear();
        list.addAll(h);
        return list;
    }

}

2.5 Mapper

此处使用 Mybatis 来与数据库进行交互

2.5.1 interface

public interface DepartmentsMapper {

    int insertBatch(@Param("list") List<Departments> departmentsList);

}

2.5.2 xml

<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
	insert into departments (department_name)
	values
	<foreach collection="list" item="item" index="index" separator=",">
          (#{item.departmentName, jdbcType=VARCHAR})
	</foreach>
</insert>

3. 前端

前端的样例,大家可以参考我下面这一篇文章的第二章节,后续只要更改访问后台的链接即可

SpringBoot + Vue + MinIO 实现文件上传:https://blog.csdn.net/wanzijy/article/details/127601558

4. 不足之处

上面虽然是可以实现表格数据的导入,但如果有人去细心观察过后台的日志就会发现,一些表格中的空白行也会进行解析

虽然本人有对每一行数据进行判空的操作,但是如果可以知道他是空白行,那么就可以省去进行判空的时间,减少资源的损耗

那时本人一直都没有琢磨出来,有懂的博主们,可以私信与我沟通交流下

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

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

相关文章

使用vi、vim、sed、echo、cat操作文件

记录&#xff1a;324 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用vi编辑器、vim编辑器、sed编辑器操作文件读、写、删、替换等操作&#xff1b;使用echo命令和cat命令将内容输出文件并查看内容。 版本&#xff1a; 操作系统&#xff1a;CentOS 7.9 1.vi编辑器…

同花顺_代码解析_技术指标_R

本文通过对同花顺中现成代码进行解析&#xff0c;用以了解同花顺相关策略设计的思想 目录 RAD RADER RCCD ROC ROCFS RSI RSIFS RAD 威力雷达 大盘指标。 RAD的判断基准法与传统指标相似: 白线上穿黄线为金叉,示强势,为买入建仓机会参考&#xff1b; 白线下穿黄线为…

红黑树的插入(C++实现)

1. 红黑树 1.1 概念 红黑树是一种二叉搜索树&#xff0c;它是AVL树的优化版本。红黑树是每个节点都带有颜色属性的二叉搜索树&#xff0c;颜色为红色或黑色。 之所以选择“红色”是因为这是作者在帕罗奥多研究中心公司Xerox PARC工作时用彩色雷射列印机可以产生的最好看的颜色…

Java学习之包访问修饰符

基本介绍 java 提供四种访问控制修饰符号&#xff0c;用于控制方法和属性(成员变量)的访问权限&#xff08;范围&#xff09; 公开级别:用 public 修饰,对外公开受保护级别:用 protected 修饰,对子类和同一个包中的类公开默认级别:没有修饰符号,向同一个包的类公开.私有级别:…

采用sFlow工具实现流量监控--实验

采用sFlow工具实现流量监控--实验采用sFlow工具实现流量监控---实验学习目标学习内容实验原理实验拓扑实验仿真启动sFlow-rt以及floodlight控制器创建拓扑部署sFlow agent步骤1.步骤2.步骤3步骤4步骤5.步骤6.总结申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&…

C++模拟OpenGL库——图形学状态机接口封装(一):用状态模式重构部分代码及接口定义

目录 什么是状态机&#xff1f; 基于状态机模式进行重构 Canvas.h源码 什么是状态机&#xff1f; 回顾之前两部分内容&#xff0c;我们做了&#xff1a; 绘制点绘制线&#xff08;Brensenham&#xff09;绘制三角形&#xff08;拆分法&#xff09;图片操作&#xff08;stb…

RabbitMQ------延迟队列(整合SpringBoot以及使用延迟插件实现真正延时)(七)

RabbitMQ------延迟队列&#xff08;七&#xff09; 延迟队列 延迟队列&#xff0c;内部是有序的&#xff0c;特点&#xff1a;延时属性。 简单讲&#xff1a;延时队列是用来存放需要在指定时间被处理的元素队列。 是基于死信队列的消息过期场景。 适用场景 1.订单在十分钟…

Linux(centos7)安装MySQL5.7

Linux 安装MySQL5.7 数据库 所有的安装方式是基于手动式的安装&#xff0c;也就是整体的下载然后配置 rpm与yum之间的关系 rpm 是Linux 免除编译安装带来的安装方式&#xff0c;而yum 是在rpm 上面的进一步的优化&#xff0c;换句话说yum 既包含了rpm 的简单安装&#xff0c…

百度地图自定义覆盖物(html)格式

<style type"text/css"> body, html{ width: 100%; height: 100%; overflow: hidden; margin: 0; font-family: "微软雅黑"; display: flex; justify-content: space-between; } #cont…

使用html+css实现一个静态页面(厦门旅游网站制作6个页面) 旅游网页设计制作 HTML5期末考核大作业,网站——美丽家乡。 学生旅行 游玩 主题住宿网页

&#x1f468;‍&#x1f393;静态网站的编写主要是用 HTML DⅣV CSSJS等来完成页面的排版设计&#x1f469;‍&#x1f393;&#xff0c;一般的网页作业需要融入以下知识点&#xff1a;div布局、浮动定位、高级css、表格、表单及验证、js轮播图、音频视频Fash的应用、uli、下拉…

FL Studio2023水果完整中文版音乐制作软件

FL Studio2023水果中文版是一款由 Image Line 公司研发几近完美的虚拟音乐工作站,同时也是知名的音乐制作软件。它让你的计算机就像是全功能的录音室&#xff0c;漂亮的大混音盘&#xff0c;先进的创作工具&#xff0c;让你的音乐突破想象力的限制。它可以播放由你指定或加入的…

IP包头分析

数据来源 IP包头长度 ip包头的长度在20-60个字节间&#xff0c;一般是20字节&#xff08;固定部分&#xff09;&#xff0c;可选项最大是40个字节&#xff08;比较少用&#xff09;。 第一行 版本 就是指出IP数据包是什么版本&#xff1b;常见的版本就是0100 IPV4和 0110 IPV6…

机器学习中基本符号表示和常用术语

目录一. 基本符号表示二. 常用术语1. 精准率计算&#xff08;precision&#xff09;2.召回率计算&#xff08;recall&#xff09;3.准确率的计算&#xff08;accuracy&#xff09;4.F1 Score5. G分数6.一. 基本符号表示 TP &#xff08;true positive&#xff09;&#xff1a;预…

【Python】基础语法(安装,常变量,类型,注释,运算符)

目录python环境搭建安装Python安装pycharmpython基础语法常量和表达式变量和数据类型变量数据类型注释输入输出运算符算术运算符关系运算符逻辑运算符赋值运算符xdm,最近更新一些学习Python基础知识的内容,感谢支持!python环境搭建 俗话说工欲善其事必先利其器,要想学习Python开…

新知实验室TRTC初体验

小记 一次偶然的邂逅,让我知道了TRTC实时音视频这个神奇的东西,于是便开始研究起来这个鬼东西,本以为是一个很简单的东西,调用一下SDK就完事了 , 谁知道它的文档并不是很齐全,这一点还需要多多努力啊!!! 正文 实时音视频&#xff08;TRTC&#xff09; 是腾讯云提供的一套低…

现代对称密码

乘积密码 因为语言特性&#xff0c;用代替和置换是不安全的&#xff0c;可以考虑用多次的加密增强密码强度。多次加密想要提高密码强度&#xff0c;要求多次加密不能成为一个群&#xff0c;那么加密就可以被重复并且组合复杂度会增加。 分组密码 分组密码就是把明文分组后进…

Linux进阶-Shell编程与环境变量

目录 定义变量&#xff1a; 使用变量&#xff1a; 将命令的结果赋值给变量&#xff1a; 删除变量&#xff1a;unset 退出当前进程&#xff1a;exit 读取从键盘输入的数据 &#xff1a;read 对整数进行数字运算&#xff1a;(()) 逻辑与或&#xff1a; 检测某个条件是否成…

【Java八股文总结】之MySQL数据库

文章目录数据库一、基本概念二、MySQL数据库2.1 MySQL基础1、MySQL数据库的优点&#xff1f;2、MySQL支持的数据类型有&#xff1f;Q&#xff1a;varchar 和 char 的区别&#xff1f;Q&#xff1a;blob 和 text 的区别&#xff1f;Q&#xff1a;datetime 和 timestamp 的区别&a…

DI依赖注入-P8,P9,P10,P11

1.构造器注入 之前写过了~~~~ 2.Set方式注入【重点】 3.拓展方式注入 2.Set方式注入【重点】 【环境搭建】 1.复杂类型 2.真实测试对象 四个文件 Student实体类的创建&#xff1a; 主要是依据官方文档来建立。那个Address也是为了测试不同的类型&#xff0c;而创建的引…

攻防世界misc2-1

misc2-1 题目描述&#xff1a;无 题目环境&#xff1a;https://download.csdn.net/download/m0_59188912/87094620 打开图片&#xff0c;发现无法显示。 使用winhex打开&#xff0c;从其中一段看出这是逆序图片。 使用python脚本将其正序排列。 脚本源码&#xff1a; f1open(‘…