背景:工作中,经常发现需要将excel中的表数据导入到mysql中,实际操作一般都是用navcat,但是使用中也发现navcat只支持单个表导入,对xlsx格式支持不友好。于是写了这么一个导表工具。在此,重新记录一下过程。

先说一下结果:

1. 支持批量文件导入

2. 目前只支持单个数据库导入,稍微改动一下可以支持多个数据库

3. 只支持xlsx格式,改动下可以支持xls格式

效果大概是这样

过程如下:

一:设计前端页面

这个我不是很懂,多年来对前端江湖不敢入。只会一些js操作, css那一块完全不懂。所幸导表工具不需要很复杂的页面,只需要一个简单的上传按钮就可以了,网上找一些页面,简单修改下即可使用,这个略去

二:服务端设计

先说一下具体思路

先将excel文件上传到服务器

服务器解析文件得到sql文件

写入sql文件

项目目录如下

其中,main.go是启动文件, www目录下是静态文件,excel2db_app.go包含一个Start方法,excel2db包含主要逻辑。

下载必须的类库,直接用go get命令即可,主要是这两个

"github.com/go-sql-driver/mysql"

"github.com/tealeg/xlsx"

main.go文件,直接调用excel2db_app.go的Start()方法即可,这里略去

excel2db_app.go文件,

主要代码如下

package excel2db

import (

"fmt"

"mime/multipart"

"net/http"

//"os"

//"io"

"html/template"

"log"

//"time"

"io/ioutil"

"strconv"

"strings"

)

func upload(w http.ResponseWriter, r *http.Request) {

r.ParseForm()

if r.Method == "GET" {

t, err := template.ParseFiles("./app/excel2db/www/input_sql.html")

checkErr(err)

t.Execute(w, nil)

} else {

r.ParseMultipartForm(32 << 20)

mp := r.MultipartForm

if mp == nil {

log.Println("not MultipartForm.")

w.Write(([]byte)("不是MultipartForm格式"))

return

}

go action(w, mp)

}

}

func action(w http.ResponseWriter, mp *multipart.Form) {

fhs := mp.File["file"]

num := len(fhs)

log.Printf("总文件数:%d\n", num)

for n, fheader := range fhs {

fmt.Printf("%d : %s\n", n, fheader.Filename)

if !strings.HasSuffix(fheader.Filename, ".xlsx") {

continue

}

uploadFile, err := fheader.Open()

checkErr(err)

data, err := ioutil.ReadAll(uploadFile)

checkErr(err)

// defer func() {

// if e := recover(); e != nil {

// fmt.Printf("Panicing %s\r\n", e)

// }

// }()

readExcelFile(data)

}

w.Write(([]byte)("成功"))

log.Println("upload success")

}

func checkErr(err error) {

if err != nil {

// err.Error()

log.Println(err)

}

}

func Start() {

fmt.Println("----Excel to Sql multiple工具启动中----")

http.Handle("/js/", http.FileServer(http.Dir("app/excel2db/www/")))

http.Handle("/css/", http.FileServer(http.Dir("app/excel2db/www/")))

http.HandleFunc("/upload", upload)

port := 8888

log.Println("欢迎使用 http://127.0.0.1:" + strconv.Itoa(port) + "/upload")

err := http.ListenAndServe(":"+strconv.Itoa(port), nil)

if err != nil {

log.Fatal("listenAndServe: ", err)

}

}

可以看到,就是启动了一个http服务器

excel2db.app文件,代码如下

package excel2db

import (

"bytes"

"database/sql"

"fmt"

"log"

_ "strconv"

"strings"

_ "strings"

_ "github.com/go-sql-driver/mysql"

"github.com/tealeg/xlsx"

)

const user_name = "zjs"

const password = "123456"

const db_url = "127.0.0.1:3306"

const db_name = "ddd2h5_dev"

func excelToDb(fileNames []string) {

// 先将Excel读取

for _, fname := range fileNames {

fmt.Println("====", fname)

xlFile, err := xlsx.OpenFile(fname)

checkErr(err)

for _, sheet := range xlFile.Sheets {

tbName := sheet.Name

fmt.Println("表名:", tbName)

var buffer bytes.Buffer

buffer.WriteString("insert into ")

buffer.WriteString(tbName)

for rowIndex, row := range sheet.Rows {

if rowIndex == 1 {

buffer.WriteString(" (")

for _, cell := range row.Cells {

// 0行是中文解释, 1行是字段名

text := cell.String()

fmt.Printf("%s\n", text)

buffer.WriteString(text)

buffer.WriteString(",")

}

buffer.WriteString(")")

}

}

}

}

}

func readExcelFile(data []byte) {

xlFile, err := xlsx.OpenBinary(data)

checkErr(err)

tabMap := getAllTables()

for _, sheet := range xlFile.Sheets {

tbName := sheet.Name

_, isExist := tabMap[tbName]

if !isExist {

continue

}

fmt.Println("表名:", tbName)

colMap1 := make(map[string]int) // 当成一个set, 判断是否存在column

colMap2 := make(map[int]string) // 真正的map,得到位置与column的关系

// 根据表名得到栏目名,一般情况下excel会比db表多出一些字段

err := getColumnName(tbName, colMap1)

if err != nil {

log.Println("查找表报错-----", tbName)

log.Println(err)

continue

}

var buffer bytes.Buffer

buffer.WriteString("insert into ")

buffer.WriteString(tbName)

buffer.WriteString(" (")

var findColumnOver = false

var lastColNumber = -12

for _, row := range sheet.Rows {

// 直接循环,直到碰到与栏目名对应的那一行

if !findColumnOver {

for index, cell := range row.Cells {

text := cell.String()

_, exist := colMap1[text]

if exist {

colMap2[index] = text

buffer.WriteString("`")

buffer.WriteString(text)

buffer.WriteString("`")

if len(colMap1) == len(colMap2) {

findColumnOver = true

lastColNumber = index

buffer.WriteString(") values ")

} else {

buffer.WriteString(",")

}

}

}

if len(colMap2) != len(colMap1) {

//数据不相等,栏目名找错了,直接清空

colMap2 = make(map[int]string)

buffer.Reset()

buffer.WriteString("insert into ")

buffer.WriteString(tbName)

buffer.WriteString(" (")

}

} else {

// 说明这时可以导数据了

isBlankColumn := false

for ind, cell := range row.Cells {

_, exist := colMap2[ind]

if !exist {

continue

}

text := cell.String()

if ind == 0 && strings.Compare(text, "") == 0 {

// 空白数据

isBlankColumn = true

break

}

if ind == 0 {

buffer.WriteString("(")

}

buffer.WriteString("\"")

buffer.WriteString(text)

buffer.WriteString("\"")

if ind == lastColNumber {

buffer.WriteString(")")

break

} else {

buffer.WriteString(",")

}

}

// 判断是否还有其他数据

if len(row.Cells) != 0 && !isBlankColumn {

buffer.WriteString(",")

}

}

}

str := buffer.String()

sql := str[0 : len(str)-1] // 把','去掉

//log.Println("jieguo: ", sql)

// 先清空表数据

clearTable(tbName)

// 插入新数据

insertToDb(sql)

}

}

var MY_DB *sql.DB = nil

func getDb() *sql.DB {

if MY_DB == nil {

//db, err := sql.Open("mysql", "zjs:123456@tcp(127.0.0.1:3306)/ddd2h5_dev?charset=utf8")

db, err := sql.Open("mysql", user_name+":"+password+"@tcp("+db_url+")/"+db_name+"?charset=utf8")

if err != nil {

log.Fatal("数据库链接出错: ", err)

}

//checkErr(err)

MY_DB = db

}

return MY_DB

}

func getAllTables() map[string]int {

tabMap := make(map[string]int)

rows, err := getDb().Query("show tables")

if err != nil {

log.Println("得到全部表名报错:", err)

}

for rows.Next() {

var tabName string

rows.Scan(&tabName)

tabMap[tabName] = 0

}

return tabMap

}

func getColumnName(tbName string, colMap map[string]int) error {

rows, err := getDb().Query("select * from " + tbName + " limit 1")

if err != nil {

return err

}

//返回所有列

cols, _ := rows.Columns()

for _, col := range cols {

colMap[col] = 0

}

return nil

}

func clearTable(tbName string) {

_, err := getDb().Exec(" DELETE FROM " + tbName)

if err != nil {

log.Println("清空表报错: ", err)

}

}

func insertToDb(sql string) {

_, err := getDb().Exec(sql)

if err != nil {

log.Println("插入数据报错:", err)

}

}

excel转mysql 工具_一个简单的批量excel转mysql工具相关推荐

  1. php使用mysql怎么连接浏览器_一个简单的php实现的MySQL数据浏览器

    一个简单的php实现的MySQL数据浏览器 更新时间:2007年03月11日 00:00:00   作者: 这个程序可以用来浏览MySQL中的数据,您可以稍做修改就可以做出很不错的MySQL浏览器. ...

  2. php简单的mysql类_一个简单的php mysql操作类

    本文分享一个简单的php.mysql操作类,很简单,主要是数据的连接.查询等.有需要的朋友参考下吧. 分享一段php.mysql操作类的代码,供初学的朋友参考. 一个简单的类使用php和mysql数据 ...

  3. mysql浏览器_一个简单的MySQL数据浏览器

    一个简单的MySQL数据浏览器 更新时间:2006年10月09日 00:00:00   作者: 这个程序可以用来浏览MySQL中的数据,您可以稍做修改就可以做出很不错的MySQL浏览器. */ /* ...

  4. php+mysql记事本_一个简单记事本php操作mysql辅助类创建

    //SqlHelper.class.phpconn=mysql_connect($this->host,$this->user,$this->passwrd); if(!$this- ...

  5. mvc登录实例 mysql_spring mvc + mybatis + mysql 调整的一个简单的登录例子

    spring mvc + mybatis + mysql 整合的一个简单的登录例子 今天用spring跟mybatis整合写了一个简单的登录例子,第一次整合,给自己做个笔记,可能注释写的有点少,做的不 ...

  6. 用计算机制作动画,如何使用制作工具制作一个简单的Flash动画-电脑自学网

    怎么制作Flash动画?通过Adobe Flash我们可以制作出非常有趣好看的动画,也可以制作一键简单的小动画,下面给大家介绍如何使用制作工具制作一个简单的Flash动画. 操作方法: 1.打开fla ...

  7. Matlab中如何使用appdesigner设计工具建立一个简单的界面

    Matlab中如何使用appdesigner设计工具建立一个简单的界面(数据的输入.处理和保存) 以使用不同算法处理图像的功能为例 建立一个新的空白界面,matlab中输入appdesigner,打开 ...

  8. 如何实现一个简单好用的roadmap制作工具

    周末实现的一个简单好用的roadmap制作工具, https://fancylife.github.io/z-roadmap/  ,开源给大家

  9. [RK3288][Android7.1]在Root用户下的一个简单更改开机动画的小工具

    [RK3288][Android7.1]在Root用户下的一个简单更改开机动画的小工具 Platform: Rockchip OS: Android 7.1.2 Kernel: 4.4 需求: 在使用 ...

最新文章

  1. UIMenuController的简单使用
  2. python数字编码_Python 编码为什么那么蛋疼?
  3. nginx 中location中root和alias的区别
  4. arduino char*转string_Java 中 String 类的常用方法汇总
  5. excel批量更改超链接_批量新建Excel指定名称工作表并设置超链接!你,学会了吗?...
  6. 网站集成QQ登录功能
  7. ubuntu-16.04安装程序报错 you might want to run 'apt-get -f install' to correct these
  8. 复制pdf文本出现大量换行的解决办法
  9. 渗透工具——Namp基础用法
  10. MFC绘制bmp图片背景
  11. B75经典门户商业版Discuz模板下载
  12. 微信小程序自定义picker
  13. Chrome打包扩展程序错误,清单文件缺失或不可读
  14. 微信商户平台结算周期T+1是什么意思
  15. 集合转换成数组的两种方法---toArray()和toArray(T[] a)
  16. [JZOJ5710] Mex
  17. 移动CRM产品同质化严重,市场一片红海
  18. java选择题《每日一练》
  19. 爆糗的买单,看谁脸皮厚
  20. 2013的联想台式计算机,联想台式电脑选购常识_联想台式电脑使用常识 -真快乐商城...

热门文章

  1. wsappx关不掉_win10系统下wsappx.exe进程占用内存大能关闭吗
  2. 【演示动画制作】Focusky教程 | focusky交互功能教程
  3. 7 款颜值当道的 Linux 操作系统 !
  4. eclipse中日志无法打印到文件中的解决办法
  5. VOD(视频点播技术)基本原理
  6. 【问题解决】ESP32开发板上的CP210xUSB转串口坏了怎么办
  7. emwin 部分汉字编译时错误解决办法。
  8. python封装一个小游戏
  9. Devops系统化,从零开始学习容器技术(更新中)
  10. PCM音频数据、DSD音频数据,spdif,以及DOP格式说明