使用go语言来完成复杂excel表的导出导入

使用go语言来完成复杂excel表的导出导入(一)

1.复杂表的导入

   开发需求是需要在功能页面上开发一个excel文件的导入导出功能,
   这里的复杂指定是表内数据夹杂着一对多,多对一的形式,如下图所示。数据杂乱而且对应不统一。

在这里插入图片描述
首先我们先设计一个页面处理器,也就是一个前端页面用来上传要处理的excel文件,注意这里应该是可以处理多个Sheet,代码如下图。

func UploadPage(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodGet {
		// 渲染上传页面
		tmpl := template.Must(template.ParseFiles(filepath.Join("template", "index.html")))
		err := tmpl.Execute(w, nil)
		if err != nil {
			http.Error(w, fmt.Sprintf("Failed to render template: %v", err), http.StatusInternalServerError)
		}
	}
}

这段代码的主要作用就是用户在前端点击按钮之后,接收一个excel文件。接收文件之后,就进行处理。
首先就是先从上传的excel文件中获取所有的Sheet,然后再进行数据库的连接,
根据表的特性设计代码提取每一列的数据插入到数据库中,在这里为了确保插入时表的完整性需要使用数据库的事务,我还在这里添加了检查数据是否重复的报错功能,来确保数据行列中不会出现重复数据。代码如下,

func ImportData(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
		return
	}

	// 从前端上传文件
	file, _, err := r.FormFile("excelFile")
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get form file: %v", err), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	// 打开 Excel 文件
	f, err := excelize.OpenReader(file)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to open Excel file: %v", err), http.StatusInternalServerError)
		return
	}

	// 获取所有工作表名称
	sheetNames := f.GetSheetMap()
	if len(sheetNames) == 0 {
		http.Error(w, "No sheets found in Excel file", http.StatusInternalServerError)
		return
	}

	// 连接 MySQL 数据库
	dsn := "root:root@tcp(127.0.0.1:3306)/database_4"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to connect to database: %v", err), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	// 确保连接有效
	if err := db.Ping(); err != nil {
		http.Error(w, fmt.Sprintf("Failed to ping database: %v", err), http.StatusInternalServerError)
		return
	}

	for _, sheetName := range sheetNames {
		// 开始事务
		tx, err := db.Begin()
		if err != nil {
			http.Error(w, fmt.Sprintf("Failed to begin transaction: %v", err), http.StatusInternalServerError)
			return
		}

		// 读取工作表
		rows := f.GetRows(sheetName)
		if len(rows) == 0 {
			http.Error(w, fmt.Sprintf("No rows found in sheet: %v", sheetName), http.StatusInternalServerError)
			tx.Rollback()
			continue
		}

		// 使用 map 分别检查 SubSpanNumber 和 BeamNumber 的重复
		subSpanNumberSet := make(map[string]int)
		beamNumberSet := make(map[string]int)

		// 变量来跟踪当前的 spanNumber 和 beamNumber
		var currentSpanNumber string
		var currentBeamNumber string

		// 跳过前两行
		for i, row := range rows[2:] {
			// 跳过空行
			if len(row) == 0 {
				continue
			}

			// 检查行的长度是否足够go
			if len(row) < 17 {
				log.Printf("Row %d does not have enough columns: %v", i+2, row)
				continue
			}

			// 提取每一列的数据
			id := row[0]
			bridgeName := row[1]
			centerPileNumber := row[2]
			beamType := row[3]
			subSpanNumber := row[4]
			spanNumber := row[5]
			beamNumber := row[6]
			designBeamLength, _ := strconv.ParseFloat(row[7], 64)
			designBeamHeight, _ := strconv.ParseFloat(row[8], 64)
			topPlateWidth, _ := strconv.ParseFloat(row[9], 64)
			bottomPlateWidth, _ := strconv.ParseFloat(row[10], 64)
			webThickness, _ := strconv.ParseFloat(row[11], 64)
			flangeThickness, _ := strconv.ParseFloat(row[12], 64)
			camber := row[13]
			expansionJointType := row[14]
			concreteStrength := row[15]
			concreteUsage, _ := strconv.ParseFloat(row[16], 64)

			// 如果 spanNumber 变化,重置 subSpanNumberSet
			if spanNumber != currentSpanNumber {
				subSpanNumberSet = make(map[string]int)
				currentSpanNumber = spanNumber
			}

			// 如果 beamNumber 变化,重置 beamNumberSet
			if beamNumber != currentBeamNumber {
				beamNumberSet = make(map[string]int)
				currentBeamNumber = beamNumber
			}

			// 跳过 spanNumber 为空格的检查,但仍保留其他数据插入
			if spanNumber != "" {
				// 检查 spanNumber 是否重复
				if _, found := subSpanNumberSet[spanNumber]; found {
					http.Error(w, fmt.Sprintf("在 %v 表 联跨编号列 第%d行出现数据重复: %v", sheetName, i+3, subSpanNumber), http.StatusInternalServerError)
					tx.Rollback()
					return
				}
				subSpanNumberSet[spanNumber] = i + 3 // 保存行号以便于调试
			}

			// 检查 BeamNumber 是否重复
			if _, found := beamNumberSet[beamNumber]; found {
				http.Error(w, fmt.Sprintf("在 %v 表 梁体编号列 第%d行出现数据重复: %v", sheetName, i+3, beamNumber), http.StatusInternalServerError)
				tx.Rollback()
				return
			}
			beamNumberSet[beamNumber] = i + 3 // 保存行号以便于调试

			// 插入数据到数据库
			query := `
            INSERT INTO bridge_data (
                id, bridge_name, center_pile_number, beam_type, sub_span_number, span_number, beam_number,
                design_beam_length, design_beam_height, top_plate_width, bottom_plate_width, web_thickness,
                flange_thickness, camber, expansion_joint_type, concrete_strength, concrete_usage, filename
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            `
			_, err = tx.Exec(query, id, bridgeName, centerPileNumber, beamType, subSpanNumber, spanNumber, beamNumber,
				designBeamLength, designBeamHeight, topPlateWidth, bottomPlateWidth, webThickness,
				flangeThickness, camber, expansionJointType, concreteStrength, concreteUsage, sheetName)
			if err != nil {
				log.Printf("Failed to insert row %d: %v", i+3, err)
				http.Error(w, fmt.Sprintf("Failed to insert row %d: %v", i+3, err), http.StatusInternalServerError)
				tx.Rollback()
				return
			}
		}

		// 插入成功后,添加标志行
		currentTime := time.Now().Format(time.RFC3339)
		fileName := sheetName
		status := "success"

		markQuery := `
        INSERT INTO process_mark (
            timestamp, filename, status
        ) VALUES (?, ?, ?)
        `
		_, err = tx.Exec(markQuery, currentTime, fileName, status)
		if err != nil {
			log.Printf("Failed to insert process mark for sheet %s: %v", sheetName, err)
			http.Error(w, fmt.Sprintf("Failed to insert process mark for sheet %s: %v", sheetName, err), http.StatusInternalServerError)
			tx.Rollback()
			return
		}

		// 提交事务
		if err := tx.Commit(); err != nil {
			http.Error(w, fmt.Sprintf("Failed to commit transaction: %v", err), http.StatusInternalServerError)
			return
		}
	}

	fmt.Fprintln(w, "excel数据导入成功")
}

处理完成之后的效果就是(部分效果图)
在这里插入图片描述
**

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

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

相关文章

基于单片机和 Arduino 平台的六自由度可控机械手臂

摘 要 : 为了降低机械手臂的设计开发难度 &#xff0c; 并使之尽早地投入应用 &#xff0c; 设计一种基于单片机和 Arduino 平台的六自由度可控机械手臂 。提出六自由度可控机械手臂的控制方案&#xff0c; 给出机械手臂控制系统的结构框图 。 详细设计六自由度可控机械手臂…

1991java Web体检预约管理系统eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP体检预约管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使用…

2024华为数通HCIP-datacom最新题库(变题更新⑤)

请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了&#xff0c;如果你准备去考试&#xff0c;还是用的之前的题库&#xff0c;切记暂缓。 1、…

MATLAB-遗传GA-CNN-SVM,基于GA遗传优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类)

MATLAB-遗传GA-CNN-SVM&#xff0c;基于GA遗传优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类) 1.数据均为Excel数据&#xff0c;直接替换数据就可以运行程序。 2.所有程序都经过验证&#xff0c;保证程序可以运行。 3.具有良好的编程习惯&#xf…

linux centos rabbitmq3.7.5 一键安装部署

linux centos rabbitmq3.7.5 一键安装部署 一、基础理论二、kafka和rocketmq、rabbitmq的区别三、下载所需安装包四、一键安装 一、基础理论 RabbitMQ是一个实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff0c;其作用主要体现在以下几个方面&a…

Docker 查看源地址/仓库地址,更改

一、源地址文件配置路径。若有docker文件夹&#xff0c;没有json&#xff0c;可以新增&#xff0c;复制进去内容 /etc/docker/daemon.json {"registry-mirrors": ["https://dockerhub.azk8s.cn","https://hub-mirror.c.163.com"&#xff0c;&q…

“北京到底有谁在啊”影视APP开发,解锁最简单的快乐

随着电视剧《玫瑰的故事》在腾讯视频APP热播&#xff0c;APP也增加了很多热度&#xff0c;一款丰富的影视APP&#xff0c;无论是热门大片、经典影视剧、还是最新综艺节目&#xff0c;能畅享无限精彩的影视内容&#xff01; 开发影视APP&#xff0c;需要专业的技术服务商来解决…

k8s手撕架构图+详解

“如果您在解决类似问题时也遇到了困难&#xff0c;希望我的经验分享对您有所帮助。如果您有任何疑问或者想分享您的经历&#xff0c;欢迎在评论区留言&#xff0c;我们可以一起探讨解决方案。祝您在编程路上顺利前行&#xff0c;不断突破技术的难关&#xff0c;感谢您的阅读&a…

使用nvm切换node版本时报错:exit status 1解决办法

作者介绍&#xff1a;计算机专业研究生&#xff0c;现企业打工人&#xff0c;从事Java全栈开发 主要内容&#xff1a;技术学习笔记、Java实战项目、项目问题解决记录、AI、简历模板、简历指导、技术交流、论文交流&#xff08;SCI论文两篇&#xff09; 上点关注下点赞 生活越过…

项目开发 TCP-Socket连接功能实现(Android端)

前段时间在公司做项目的时候遇到了一个功能需要使用TCP-Socket连接硬件设备进行通信&#xff0c;查了很多资料也只是关于HTTP-Socket相关的&#xff0c;没法满足项目的要求&#xff0c;后来查到一个相关的插件&#xff0c;现在有时间和大家分享一下。 项目简单介绍&#xff1a…

基于STM32+华为云IOT设计的智能冰箱(华为云IOT)

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成【4】摘要 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 二、部署华为云物联网平台2.1 物联网平台介绍2.2 开通物联网服务2.3 创建产品&#xff08…

Stateflow快速入门系列(-):构造并运行 Stateflow 图

Stateflow 提供了一种图形语言&#xff0c;包括状态转移图、流程图、状态转移表和真值表。您可以使用 Stateflow 来说明 MATLAB 算法和 Simulink 模型如何响应输入信号、事件和基于时间的条件。 Stateflow 使您能够设计和开发监控、任务调度、故障管理、通信协议、用户界面和混…

Windows USB设备驱动开发 - 常见概念的解释

我们听到许多 USB 术语几乎交替抛出。 它们都是什么意思&#xff1f;假设我们看到类似 “多亏了 USB 3.0&#xff0c;我可以将 SuperSpeed U 盘连接到电脑的 xHCI 主机控制器&#xff0c;并更快地复制文件。” 让我们了解该句子中的 USB 术语。 USB 3.0、USB 2.0 和 USB 1.0 请…

大模型笔记1: Longformer环境配置

论文: https://arxiv.org/abs/2004.05150 首先保证电脑上配置了git. git环境配置: https://blog.csdn.net/Andone_hsx/article/details/87937329 3.1、找到git安装路径中bin的位置&#xff0c;如&#xff1a;D:\Program Files\Git\bin 找到git安装路径中git-core的…

C++ Primer 中文版 第5版 读书笔记

读书过程中发现&#xff0c;读得越多&#xff0c;忘得越多。因此记录读书笔记 1.2 初始输入输出 向流写入数据 <<运算符&#xff08;输出运算符&#xff09;接受两个运算对象&#xff1a;左侧的运算对象必须是一个ostream对象&#xff0c;右侧的运算对象是要打印的值。…

限域传质分离膜兼具高渗透性、高选择性特点 未来应用前景广阔

限域传质分离膜兼具高渗透性、高选择性特点 未来应用前景广阔 分离膜是一种具有选择性透过功能的薄层材料。限域传质分离膜是基于限域传质机制的分离膜&#xff0c;兼具高渗透性、高选择性的特点。限域传质是流体分子通过与其运动自由程相当传质空间的过程&#xff0c;流体分子…

TiDB-从0到1-数据导出导入

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇TiDB-从0到1-集群扩缩容 一、数据导出 TiDB中通过Dumpling来实现数据导出&#xff0c;与MySQL中的mysqldump类似&#xff0c;其属于…

SQL Server 2016安装【windows 11】

获取安装包 链接: https://pan.baidu.com/s/1OM9JCNah0-zrOQ0vNBkh6g 提取码: 1245 安装程序 双击点击setup.exe 弹出SQL Server安装中学界面&#xff0c;点击【安装】—点击【全新SQL Server…添加功能】 不出意外的话啊会跳到 SQL Server 2016 安装程序界面&#xff0c;直…

idea使用maven打包报错GBK不可映射字符

方法一&#xff1a;设置环境变量 打开“控制面板” > “系统和安全” > “系统”。点击“高级系统设置”。在“系统属性”窗口中&#xff0c;点击“环境变量”。在“系统变量”部分&#xff0c;点击“新建”&#xff0c;创建一个新的变量&#xff1a; 变量名&#xff1a;…

数学建模 —— 矩阵的运算(上)

目录 调用函数运算 sum : 求和函数 prod : 求乘积函数(product) cumsum : 计算累积和(cumulative sum) diff : 计算差分(difference) mean : 计算平均值 (average) median : 计算中位数 mode : 计算众数 var : 计算方差 (variance) std : 计 算 标 准 差 (standard d…