spingboot+layui+poi实现excel导入功能,并批量插入数据库

2023-11-17

1、maven的pom文件添加下面jar包

        <dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.17</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.17</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>3.17</version>
		</dependency>
		<dependency>
			<groupId>org.apache.xmlbeans</groupId>
			<artifactId>xmlbeans</artifactId>
			<version>2.4.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-collections4</artifactId>
			<version>4.1</version>
		</dependency>

2、前端页面定义layui按钮,弹出导入页面

//定义按钮
<button class="layui-btn layui-btn-sm" lay-event="uploadExcel">Excel上传</button>
//弹出层位置
 <table class="layui-table"  id="currentTableId" lay-filter="currentTableFilter"></table>
//toolbar监听事件
table.on('toolbar(currentTableFilter)', function (obj) {
         if(obj.event === 'uploadExcel'){
            layer.open({
                    type: 2,
                    title: 'Excel上传',
                    shadeClose: true,
                    shade: 0.8,
                    area: ['500px', '50%'],
                    content: 'uploadHtml',
                    btn: ['确定', '取消'],
                    yes: function (index) {
                    var formSubmit = layer.getChildFrame('form', index);
                        var submited = formSubmit.find('button')[0];
                        submited.click();
                        $('.layui-laypage-btn').click();
                    }
                });
            }
        });

3、弹出框页面代码

  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>导入excel数据</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/static/layui/lib/layui-v2.6.3/css/layui.css" media="all">
    <script src="/static/layui/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
    <script src="static/assets/js/jquery.min.js"></script>
</head>
<body class="layui-layout-body">
 
<div class="layui-upload" align="center">
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;" align="center">
        <legend>请选择您要上传的Excel文件</legend>
    </fieldset>
 
    <div style="margin-top: 30px" align="center">
        <button id="importData" class="layui-btn layui-btn-normal">导入</button>
    </div>
</div>
 
 
<script src="../../layui/layui.js" th:src="@{/layui/layui.js}"></script>
<script th:src="@{/js/jquery-3.4.0.min.js}"></script>
 
<script th:inline="javascript">
 
    ctxPath = /*[[@{/}]]*/ '';
 
    layui.use(["element", "layer", "upload"], function () {
        var layer = layui.layer;
        layui.upload.render({
            elem: "#importData",
            url: '/upload',
            size: '800',
            accept: "file",
            exts: 'xls|xlsx',
            before: function () {
                layer.load();
            },
            done: function (result) {
                if (result.code === 0) {
                    layer.msg(result.msg, {icon: 1, time: 1000}, function () {
                        var index = parent.layer.getFrameIndex(window.name);
                        parent.layer.close(index);
                        parent.$('.layui-laypage-btn').click();
                    });
                } else if (result.code === -1) {
                    layer.msg(result.msg, {icon: 5, time: 2500}, function () {
                        var index = parent.layer.getFrameIndex(window.name);
                        parent.layer.close(index);
                        parent.$('.layui-laypage-btn').click();
                    });
                }
                layer.closeAll('loading');
            }
        });
    });
 
</script>
</body>
</html>
 

4、后端controller

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.xxx.bean.Unit;
import com.xxx.common.ReadDataByExcel;
import com.xxx.service.UnitService;
import com.xxx.util.JsonResult;
import com.xxx.util.UtilTool;

import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
public class UploadController {
	@Autowired
	private UnitService unitService;
       
       //弹出页面
	@RequestMapping("/uploadHtml")
	public String uploadHtml(String mode,Model model) {
		return "upload/upload";
		
	}
	//批量数据添加
	@Transactional
    @ResponseBody
    @PostMapping(value = "upload")
    public JsonResult upload(@RequestParam MultipartFile file) {
		System.out.println(file.getOriginalFilename());
        log.info("Excel批量上传信息");
        try {
            InputStream inputStream = file.getInputStream();
            List<Map<String, Object>> list = ReadDataByExcel.readExcel(inputStream);//自定义读取excel
            List<Unit> unitsList = new ArrayList<>();
            for (Map<String, Object> map : list) {
                Set<String> keySet = map.keySet();
                Unit unit = new Unit();
                for (String string : keySet) {
                    String stt = (String) map.get(string);
                    String str = stt.trim();
                     if (string.equals("单位")) {
						unit.setUnit(str);
					}else if (string.equals("地区")) {
						unit.setRegin(str);
					}else if (string.equals("地域")) {
						unit.setReach(str);
					}else if (string.equals("情况说明")) {
						unit.setRemark(str);
					}
                }
                unit.setRegin(UtilTool.getRegin(unit.getRegin()));
                unit.setReach(UtilTool.getReach(unit.getRegin(), unit.getReach()));
                unitsList.add(unit);
            }
            
            
            //批量插入
            boolean i = unitService.saveBatch(unitsList);
            if (i) {
                log.info("数据导入成功");
                return JsonResult.success("数据导入成功");
            } else {
                log.error("数据导入失败!");
                return JsonResult.error(-1,"数据导入失败!");
            }
        } catch (Exception e) {
            log.error("数据导入出现异常:{}", e.getMessage());
            return JsonResult.error(-1,"数据导入失败,请检查模板数据格式!");
        }
    }
}

5、自定义读取excel 里面数据 (poi)


import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class ReadDataByExcel {
	public static List<Map<String, Object>> readExcel(InputStream inputStream) {
		List<Map<String, Object>> list = new ArrayList<>();
		try {
			XSSFWorkbook wb0 = new XSSFWorkbook(inputStream);
			// 循环Sheets,默认取0,第一个sheet
			// 获得Excel表的sheet的数目
			int numberOfSheets = wb0.getNumberOfSheets();
			for (int i = 0; i < numberOfSheets; i++) {
				//获取sheet工作表
				Sheet sheet = wb0.getSheetAt(i);
				//获取sheet中第一行行号,表头行  0
	    		int firstRowNum = sheet.getFirstRowNum();
	    		//获取最后一行行号 ,从0开始,20行  ==0---19
				int lastRowNum = sheet.getLastRowNum();
				// 获取表头数据行
				Row rowHead = sheet.getRow(firstRowNum);
				for (int j = firstRowNum; j < lastRowNum; j++) {  //表头不用参与遍历,所有循环少一次
					Row row = sheet.getRow(j + 1);//获取第二行
					if (rowHead!=null && !isRowEmpty(row)) {
						Map<String, Object> map = new LinkedHashMap<>();
						//rowHead.getLastCellNum()获取最后一个不为空的列是第几个 从0开始,比实际大一 如:10列==0--10
						//rowHead.getPhysicalNumberOfCells()获取不为空的列的个数
						for (int k = 0; k < rowHead.getLastCellNum(); k++) {
							// 获取表头和数据
							Cell cellHead = rowHead.getCell(k);
							Cell cell = row.getCell(k);
							String cellValue = null;
							String headValue = null;
							// 处理各种带空数据
							if(cellHead!=null) {
							headValue = cellHead.toString().trim().replaceAll(" ", "");
							}else {
							headValue="";	
							}
							if (cell != null && !cell.toString().trim().isEmpty()) {
								cellValue = cell.toString().trim().replaceAll(" ", "");
							} else {
								cellValue = "";
							}

							// map封装
							map.put(headValue, cellValue);
						}
						// list封装
						list.add(map);
					}
				}
			}
			try {
				inputStream.close();
			} catch (Exception e) {
				log.error("exception:", e);
			}
		} catch (Exception e) {
			log.error("exception:", e);
		}
		return list;
	}
	
	//判断表行是否为空
	public static boolean isRowEmpty(Row row) {
		for(int i = row.getFirstCellNum();i<row.getLastCellNum();i++) {
			Cell cell =row.getCell(i);
			if(cell!=null&&cell.getCellType()!=Cell.CELL_TYPE_BLANK) {
				return false;
			}		
		}
		return true;
	}

}

如果在读取excel数据的时候关于一大片的合并区域,poi只会返回左上角第一个cell的数据,其余的全都返回空

注意:
HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls;

XSSFWorkbook:是操作Excel2007后的版本,扩展名是.xlsx;

SXSSFWorkbook:是操作Excel2007后的版本,扩展名是.xlsx;

当数据量超出65536条后,在使用HSSFWorkbook或XSSFWorkbook,程序会报OutOfMemoryError:Javaheap space;内存溢出错误。这时应该用SXSSFworkbook。

判断excel版本除了后缀判断,也可以使用一下方法判断

      /**
 * 优化excel类型判断
 * 注意可能会获取报错 getFileMagic() only operates on streams which support mark(int)
 * 原因InputStream中markSupported方法返回值为false造成的,BufferedInputStream中返回值是true,所以改为InputStream is = new BufferedInputStream(multipartFile.getInputStream())
 */
//InputStream is = new BufferedInputStream(multipartFile.getInputStream());
 FileMagic fileMagic = FileMagic.valueOf(inputStream);
 if (Objects.equals(fileMagic, FileMagic.OLE2)) {
            return EXCEL2003;
 } else if (Objects.equals(fileMagic, FileMagic.OOXML)) {
            return EXCEL2007;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spingboot+layui+poi实现excel导入功能,并批量插入数据库 的相关文章

  • 在 Java/Android 中检查字符串是否包含 URL 的最佳方法是什么?

    在 Java Android 中检查字符串是否包含 URL 的最佳方法是什么 最好的方法是检查字符串是否包含 com net org info 其他 或者有更好的方法吗 url 输入到 Android 中的 EditText 中 它可以是粘
  • 如何在 Spring 5 MVC 中将 FilePart 转换为 byte[]

    我有从网络表单接收和上传文件的控制器方法 如何从 FilePart 中提取字节数组并将其保存到数据库 我可以通过使用 FilePart transferTo 将 FilePart 保存到文件中来完成此操作 但这看起来又慢又难看 有更好的方法
  • Cassandra Pojo Sink Flink 中的动态表名称

    我是 Apache Flink 的新手 我正在使用 Pojo Sink 将数据加载到 Cassandra 中 现在 我在以下命令的帮助下指定表和键空间名称 Table注解 现在 我想在运行时动态传递表名称和键空间名称 以便可以将数据加载到用
  • 在 Java 中实现排列算法的技巧

    作为学校项目的一部分 我需要编写一个函数 该函数将接受整数 N 并返回数组 0 1 N 1 的每个排列的二维数组 该声明看起来像 public static int permutations int N 该算法描述于http www usn
  • ProcessBuilder 未正确执行 Java 类文件

    在一个java文件中 我调用命令行语句来执行另一个java文件 这就是我正在做的 List
  • Java Swing透明JPanel问题

    我有一个 JLayeredPane 其中添加了 3 个 JPanel 我将 JPanel 设为透明 未设置背景并 setOpaque false 我在 JPanel 上绘制线条 只有最后添加的 JPanel 上的线条可见 其他 JPanel
  • 杰克逊.将缺失的属性反序列化为空Optional

    假设我有一堂这样的课 public static class Test private Optional
  • 可以显式删除 lambda 的序列化支持

    As 已经知道 https stackoverflow com a 22808112 2711488很容易添加序列化当目标接口尚未继承时支持 lambda 表达式Serializable 就像 TargetInterface Seriali
  • Encog - 如何加载神经网络的训练数据

    The NeuralDataSet我在实际中看到的对象除了 XOR 之外什么都没有 它只是两个小数据数组 我无法从文档中找出任何内容MLDataSet 似乎所有内容都必须立即加载 但是 我想循环遍历训练数据 直到到达 EOF 然后将其算作
  • Tomcat:具有强密码的 TLSv1.2 不起作用

    我安装了Tomcat 7 配置了对 TLSv1 2 的支持在端口 8443 上 我的连接器配置 协议 org apache coyote http11 Http11NioProtocol SSLEnabled true 方案 https 安
  • 关于java中同步的问题;何时/如何/到什么程度

    我正在开发我的第一个多线程程序 并在同步的几个方面陷入困境 我已经浏览了 oracle sun 主页上的多线程教程 以及这里的一些关于 SO 的问题 所以我相信我知道什么是同步 然而 正如我提到的 有几个方面我不太确定如何弄清楚 我以明确问
  • Java SWT 用户输入验证

    在 SWT 中进行用户输入验证时 Java 约定是什么 我读到有 FieldEditors 它们是非常方便的字段 但遗憾的是仅适用于首选项和对话框 我还了解到有一个 IValidator 接口 但它经常与数据绑定一起使用 就我而言 我的大多
  • Android 3.1 USB 主机 - BroadcastReceiver 未收到 USB_DEVICE_ATTACHED

    我经历过USB 主机的描述和示例位于developer android com http developer android com guide topics usb host html检测连接和分离的 USB 设备 如果我在清单文件中使用
  • Spring的@PreDestroy导致随机记录而不记录

    我正在使用 Spring 并且在终止时我让 PreDestroy 清理 bean 我不明白为什么日志记录有时会成功 而有时会失败 Using Log4j2 Logger log LogManager getLogger MyClass cl
  • 将 Spring ModelAttribute 应用于所有使用特定参数类型的控制器

    在 Spring Boot REST 应用程序中 我有一个TableRequest包含表格数据 GET 请求的列排序 筛选和分页详细信息的类型 它是通用的 因为它不关心所请求的具体数据是什么 它只指定通用表参数 因此它适用于许多不同的控制器
  • 如何查找给定字符串中仅出现一次的第一个字符[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Java并发锁和条件的使用

    我可以用object wait object notify and synchronized blocks解决生产者消费者类型的问题 同时我可以使用locks and conditions from java util concurrent
  • 表达式的类型必须是数组类型,但它解析为浮点数

    当我编写 Java 代码时 我遇到了困难 我觉得我不知何故把这个概念弄乱了 就像我不确定这一点 void setScore float sco sco score public void setScore float sco int id
  • 注释处理工具<-检查有效注释

    I have ColumnMetadata index 1 ColumnMetadata index 2 ColumnMetadata index 3 我必须使用 APT 检查索引号是否唯一 我不知道该怎么做 我看不懂教程 一般我在网上找资
  • POJO 支持使用omnifaces 自动完成primefaces

    我正在尝试在我的项目中使用 primefaces 自动完成组件 以避免将特定转换器写入我尝试使用的每个列表对象全能面孔 http showcase omnifaces org converters ListConverter如建议的here

随机推荐

  • 计算机网络第一章课后习题

    1 14 计算机网络有哪些常用的性能指标 速率 带宽 吞吐量 时延 时延带宽积 往返时间RTT 利用率 1 17 收发两端之间的传输距离为1000km 信号在媒体上的传播速率为2 x 10 8 m s 试计算以下两种情况的发送时延和传播时延
  • Android 开发中 Kotlin Coroutines 如何优雅地处理异常

    一 尽量少用 GlobalScope GlobalScope 是 CoroutineScope 的实现类 我们以前使用过的 launch async 函数都是 CoroutineScope 的扩展函数 GlobalScope 没有绑定任何
  • 为 Excalidraw 添加手写中文字体

    Excalidraw目前支持手写英文 不支持手写中文 导致画出的图不好看 那么 如何添加手写中文字体呢 准备一个手写中文字体 然后上传到一个存储位置 可以访问的地方 或者本地静态服务器 获取到一个可访问连接 可以放到云服务器上 可以放到Gi
  • 解决maven clean 报错 Process terminated

    设置一下maven
  • QT表格控件实例(Table Widget 、Table View)

    欢迎小伙伴的点评 相互学习 博主 本着开源的精神交流Qt开发的经验 将持续更新续章 为社区贡献博主自身的开源精神 文章目录 前言 一 图示实例 二 列表常用成员解析 三 代码实例解析 UI设计如下 mainwindow h main cpp
  • 使用Rest API设计简单的博客网站

    博客根地址 https mygithub com 对于每个用户自己的博客网站都在这个根地址后加url信息 使用Rest API进行设计 在这里设计如下API https mygithub com username articles http
  • python解释器的安装与配置

    目录 1 Python解释器安装配置 2 Python环境变量设置 3 Python解释器多个版本共存 1 Python解释器安装配置 python解释器是能够解释执行 Python代码的程序 它可以解析和执行 Python 程序 首先前往
  • c#之构造函数和析构函数

    如有错误 欢迎指正
  • 【五一创作】某头条参数破解并实现界面化搭建

    某条参数破解并实现界面化搭建 前言 效果展示 难点 参数逆向破解 signature ac signature s v web id 界面化实现 总结 前言 趁着日常闲余时间 想着搞一搞某条的反爬 练练手 想到自己很久没开发过前端界面了 有
  • JAVA线程 -- wait notify notifyAll

    在通常的代码中实现线程互斥用的较多的是synchronized synchronized this 与synchronized static Object 的区别 synchronized就是针对内存区块申请内存锁 this关键字代表类的一
  • Apache+PHP+MySQL环境搭建超详细!!!

    前言 最近在学习PHP语言 整理了一下关于环境搭建的部份 也可以选择集成环境会更方便 自己搭建环境会更好的理解原理 适合初学者 会持续更新哟 确定服务器的VC版本 一定要看 避免后面的错误 版本不一致会导致Apache在加载php包的时候出
  • AcWing 378. 骑士放置(最大独立集&&匈牙利算法)

    输入样例 2 3 0 输出样例 4 解析 题意为求最大独立集 即为总点数 最小点覆盖 include
  • 逆序输出数组元素

    题目描述 从键盘上输入10个整数 存储在一个长度为10的整型数组中 要求将输入的10个数逆序输出 如输入为 0 1 2 3 4 5 6 7 8 9 输出为 9 8 7 6 5 4 3 2 1 0 输入 10个整数 以空格分隔 输出 将输入的
  • Linux定时同步时间、定时任务、crontab

    Linux定时同步时间 定时任务 crontab 1 安装ntp 2 手动同步 3 确定服务器是否开启定时任务计划服务 4 创建 修改 删除定时任务 5 查看定时任务 6 查看定时任务的执行情况 7 定时任务备份恢复 8 停止crontab
  • RAC 网路结构 network

    Reliable Data Socket 协议配置Oracel Clusterware 的通信方式 默认使用的是UDP协议 在windows平台上默认使用TCP IP协议进行通信 用户不能使用内联网络与数据库进行通信 因为Cache Fus
  • 基于C#的数据库文件管理助手

    我们经常会遇到这样的问题 在数据库中的文件存放的是web格式或者是绝对路径 以及使用的是百度上传或者其他上传组件 造成了很多异步上传的冗余文件 如果客户需要我们导出企业官网中的产品图片 我们该如何处理 很简单 当然是自己写个工具来读取 然后
  • obsidian memos插件iphone端无法读取memos

    首先看periodic或者daily插件有没有问题 其次看dateview插件是否达到要求的0 5 9以上 如果是手机端不行但电脑端可以的话一般来说插件配置是没问题的 除非手机app有哪里不兼容 都没有问题的话可能是 memos插件在手机上
  • CMake中cmake_minimum_required的使用

    CMake中的命令cmake minimum required用于设定需要的最低版本的CMake 其格式如下 cmake minimum required VERSION
  • 与窗口大小无关的图像滤波算法

    问题 一幅24位彩色图像 其上有一些红色区域 这些区域都是相连的 为了将图像中红色且相连的部分分离出来 使用如下算法 对单个像素进行判断 若满足R 10 gt G且R 10 gt B 那么就认为该像素呈红色 因为图像原因 可能在某个位置孤立
  • spingboot+layui+poi实现excel导入功能,并批量插入数据库

    1 maven的pom文件添加下面jar包