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

news2024/11/16 18:39:14

使用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数据导入成功")
}

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

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

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

相关文章

原来链表如此简单

由于顺序表&#xff08;数组&#xff09;的插入、删除操作需要移动大量的元素&#xff0c;影响了运行效率&#xff0c;因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素&#xff0c;不需要使用地址连续的存储单元&#xff0c;因此…

从Multisim到SmartEDA:开启电路设计智能革命

随着科技的飞速发展&#xff0c;电路设计领域也迎来了前所未有的变革。从传统的Multisim软件到新兴的SmartEDA平台&#xff0c;这一转变不仅代表了技术的进步&#xff0c;更预示着电路设计领域即将解锁全新的潜能。 一、Multisim的辉煌与局限 Multisim作为一款经典的电路设计…

比亚迪电池展厅OLED透明屏项目方案

一、项目概述 为满足比亚迪电池展厅在温州地区的现代化展示需求&#xff0c;我们计划于2024年6月实施OLED透明屏项目。该项目旨在通过采用先进的OLED透明屏技术&#xff0c;为展厅提供独特且高效的展示方式&#xff0c;提升观众体验&#xff0c;展示比亚迪电池技术的先进性和创…

「6.25更新日志」JVS·智能BI、逻辑引擎(服务编排)功能更新说明

项目介绍 JVS是企业级数字化服务构建的基础脚手架&#xff0c;主要解决企业信息化项目交付难、实施效率低、开发成本高的问题&#xff0c;采用微服务配置化的方式&#xff0c;提供了 低代码数据分析物联网的核心能力产品&#xff0c;并构建了协同办公、企业常用的管理工具等&am…

使用内部转换处理事件

在异或状态下处理具有内部转换的事件 此示例显示在异或&#xff08;OR&#xff09;状态下使用内部转换处理三个事件时会发生什么。 在异或状态下处理一个事件 此示例显示内部转换的行为。该图表使用outgoing转换的隐式排序。 起初&#xff0c;图表处于休眠状态。状态A处于活…

计算机毕业设计Thinkphp/Laravel+vue高校图书馆借阅系统_i0521

图书馆借阅系统&#xff0c;主要的模块包括首页、个人中心、会员管理、会员等级管理、图书分类管理、图书信息管理、图书借阅管理、借阅服务评价管理、超时费用管理、留言板管理、系统管理等功能。系统中管理员主要是为了安全有效地存储和管理各类信息&#xff0c;还可以对系统…

web课程大作业-科技强国、航天科技

文章目录 概叙科技强国的概述实例分析 代码截图代码连接 概叙 科技强国的概述 一个科技强国在全球范围内具备领先的科技创新能力和竞争力&#xff0c;能够在多个高科技领域内引领创新潮流和技术发展。成为科技强国通常依赖于以下几个关键因素&#xff1a; 经济实力&#xff1…

液体冷却装置

每天一篇行业发展资讯&#xff0c;让大家更及时了解外面的世界。 更多资讯&#xff0c;请关注B站/公众号【莱歌数字】&#xff0c;有视频教程~~ 在这期中&#xff0c;我们的重点是液体基冷却解决方案。这些专利显示了不同发明者所关注的一些显著特征。 嵌入式微通道冷却包 一…

Centos7.9升级OpenSSH版本

升级前先多开几个ssh窗口或者打开telnet&#xff0c;因为升级ssh可能会导致ssh无法登录 一、查看当前版本 ssh -V 升级后的版本 二、开始升级OpenSSH 1.备份配置文件 cd /etc/ssh cp sshd_config{,.bak} cd /etc/pam.d cp sshd{,.bak}2.下载OpenSSH https://mirrors.aliy…

python 函数(方法):递归、匿名函数、闭包函数、装饰器

文章目录 一、递归1.1 什么是递归&#xff1f;1.2 递归的原理及作用1.3 递归的分类1.4 在使用递归时的注意事项1.5 递归的使用 二、匿名函数三、闭包函数3.1 闭包函数和正常函数的使用区别 四、装饰器&#xff08;相当于 Java 中的面向切面编程&#xff09; 一、递归 1.1 什么是…

针对VMWare无法使用鼠标功能键问题

在使用 VMWare 虚拟机的Ubuntu系统时发现无法使用许多鼠标带有额外的功能键&#xff0c;比如常用的前进后退&#xff0c;但是双系统中的Ubuntu没有问题&#xff0c;后来一搜发现是&#xff0c;虚拟系统中不支持这些功能键。因此我们对这个问题进行了解决。 解决方案 1.找到自…

使用 shell 脚本同步 yum 源建立本地私有仓库

文章目录 [toc]事出有因脚本内容前端展示 事出有因 有两方面原因做了这个事情&#xff1a; dockerhub 国内无法访问centos 7 要停止社区支持了 结合两个情况&#xff0c;不久的将来&#xff0c;可能国内也就没有对应的 yum 仓库了&#xff08;现在想找 centos 7 之前的仓库&…

DC/AC电源模块一种效率与可靠性兼备的能源转换解决方案

DC/AC电源模块都是一种效率与可靠性兼备的能源转换解决方案 DC/AC电源模块是一种能够将直流电源&#xff08;DC&#xff09;转换为交流电源&#xff08;AC&#xff09;的设备。它在现代电子设备中扮演着非常重要的角色&#xff0c;因为许多设备需要交流电源才能正常运行。无论…

主播美颜工具:美颜SDK的技术原理与应用

美颜SDK不仅大幅提升了主播的颜值&#xff0c;还极大地改善了用户的观看体验。本文将从美颜SDK的技术原理、应用场景以及未来发展趋势等方面进行深入探讨。 一、美颜SDK的技术原理 美颜SDK的核心在于其强大的图像处理技术&#xff0c;通过实时图像处理算法&#xff0c;对视频中…

无人机螺旋桨理论教学培训课程

本文档为一份详细的关于TYTO机器人公司提供的电机和螺旋桨理论及其实验操作的指南。指南首先概述了材料、实验目标以及实验的介绍部分&#xff0c;随后详细阐述了理论问题、实验步骤和附录内容。实验目的在于通过实际测试来测量和理解不同螺旋桨参数对无人机性能的影响&#xf…

OpenCV cv::Mat到 Eigen 的正确转换——cv2eigen

在进行计算机视觉项目时&#xff0c;我们经常需要处理相机位姿的变换。最近&#xff0c;我在项目中遇到了一个看似简单但实际上颇具挑战性的问题&#xff1a;从 OpenCV 的 cv::Mat 格式转换到 Eigen 库的格式。这个过程中遇到了一些问题&#xff0c;但最终找到了一个稳健的解决…

实现自动化:如何利用阿里云OSS上传文件并自动打标签

在当前数字化时代&#xff0c;数据管理变得愈发重要&#xff0c;特别是对于需要大规模存储和管理文件的场景。阿里云对象存储服务&#xff08;OSS&#xff09;作为业界领先的解决方案&#xff0c;不仅提供了稳定可靠的云存储&#xff0c;还支持丰富的扩展功能&#xff0c;如文件…

文件夹内-资源名称前加序号排列

问题&#xff1a;在文件夹下的资源可以按时间排序&#xff0c;导入unity后资源顺序会乱掉&#xff0c;不方便按顺序赋值&#xff0c;为了方便&#xff0c;通过下面方法在文件夹下统一在资源名称前按顺序加上序号 win11在文件夹内右键&#xff0c;选择——在终端中打开 输入&a…

生命在于学习——Python人工智能原理(4.7)

四、Python的程序结构与函数 4.4 函数 函数能将代码划分为若干模块&#xff0c;每一个模块可以相对独立的实现某一个功能&#xff0c;函数有两个主要功能&#xff0c;分别是降低编程难度和实现代码复用&#xff0c;函数是一种功能抽象&#xff0c;复用它可以将一个复杂的大问…

“基于下垂的多电源分布式控制系统设计”,高分资源,匠心制作,查重5%,下载可用。强烈推荐!!!

“基于下垂的多电源分布式控制系统设计”&#xff0c;高分资源&#xff0c;匠心制作&#xff0c;查重5%&#xff0c;下载可用。强烈推荐&#xff01;&#xff01;&#xff01; 摘要 社会的进步与发展&#xff0c;人们对于能源的需求和依赖越来越大。与此同时&#xff0c;国家…