golang获取svn信息,客户端发送文件到服务端,并把文件的svn版本和内容写入数据库

好久没记录碰到的问题和学习的东西了,最近用学习用golang写东西,用一个客户端发送文件到服务端,服务端按原目录创建文件,并把文件的svn信息,文件内容传入数据库,记录的原因主要是之前在网上找golang执行命令获取svn信息找了好久只有零零星星的信息,最后我是根据这些信息试出来的,所以分享一下,希望能帮到有需要的人。

client

package main

import (
	"fmt"
	"github.com/axgle/mahonia"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"regexp"
)

func main()  {

	dir := "你的文件目录(f:\xx\xx)"
	f,err := ioutil.ReadDir(dir)
	if err != nil{
		fmt.Println(err)
		return
	}
	//等接收方回应
	//for  {

			for fileIndex,file := range f {
				if file.IsDir() {
					xfiles, _ := GetAllFiles(dir+"\\"+file.Name())

					for _, fileName := range xfiles {
						createConn(file,fileName)
						fmt.Printf("获取的文件为[%s]\n", fileName)
					}
					fmt.Println(fileIndex)
				} else {
					conn,err1 := net.Dial("tcp","127.0.0.1:9090")
					if err1 != nil{
						fmt.Println("dial error:",err1)
						return
					}

					defer conn.Close()
					verision := getSvnInfo(dir+"\\"+file.Name())
					_, err = conn.Write([]byte(dir+"\\"+file.Name()+"|"+"1"+"|"+verision))
					if err != nil {
						fmt.Println("conn write server err:", err)
						break
					}
					var n int
					buf := make([]byte, 1024)
					n, err = conn.Read(buf)

					if err != nil {
						fmt.Println("conn.read err=", err)
						break
					}
					fmt.Printf("遍历文件[%s]", file.Name())
					if "ok" == string(buf[:n]) {
						sendFile(dir+"\\"+file.Name(), conn)
					}
				}


	//}


	}
	//info, err := os.Stat(dir) //获取文件属性
	//if err != nil {
	//	fmt.Println("err = ", err)
	//	return
	//}
	先发送一次文件名
	//_, err = conn.Write([]byte(info.Name()))
	//if err != nil {
	//	fmt.Println("conn Write server err : ", err)
	//	return
	//}
	//
	坐等接收对方的回应(最好是 "ok" )
	//var n int
	//buf := make([]byte, 1024)
	//n, err = conn.Read(buf)
	//if err != nil {
	//	fmt.Println("conn.Read err = ", err)
	//	return
	//}
	//if "ok" == string(buf[:n]) {
	//	//发送文件
	//	sendFile(dir, conn)
	//}

}
func createConn(file os.FileInfo,dir string)  {
	conn,err1 := net.Dial("tcp","127.0.0.1:9090")
	if err1 != nil{
		fmt.Println("dial error:",err1)
		return
	}
	verision := getSvnInfo(dir)

	defer conn.Close()
	var err error
	_, err = conn.Write([]byte(dir+"|"+"2"+"|"+verision))
	if err != nil {
		fmt.Println("conn write server err:", err)
		return
	}
	var n int
	buf := make([]byte, 1024)
	n, err = conn.Read(buf)

	if err != nil {
		fmt.Println("conn.read err=", err)
		return
	}
	fmt.Printf("遍历文件[%s]", file.Name())
	if "ok" == string(buf[:n]) {
		sendFile(dir, conn)
	}
}

func sendFile(filePath string,conn net.Conn)  {
	f,err :=os.Open(filePath)
	if err != nil{
		fmt.Println("os.open is err=",err)
		return
	}
	defer f.Close()
	buf:=make([]byte,1024*4)
	for {
		n,err:=f.Read(buf)
		if err!= nil{
			if err == io.EOF && n == 0{
				fmt.Println("文件发送完毕",filePath)
			}else {
				fmt.Println("f.Read err =",err)
			}
			return
		}
		conn.Write(buf[:n])
	}
}
func GetAllFiles(dirPth string) (files []string, err error) {
	var dirs []string
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}

	PthSep := string(os.PathSeparator)

	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
			GetAllFiles(dirPth + PthSep + fi.Name())
		} else {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}

	// 读取子目录下文件
	for _, table := range dirs {
		temp, _ := GetAllFiles(table)
		for _, temp1 := range temp {
			files = append(files, temp1)
		}
	}
	return files, nil
}

func GetFilesAndDirs(dirPth string) (files []string, dirs []string, err error) {
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, nil, err
	}

	PthSep := string(os.PathSeparator)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写

	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
			GetFilesAndDirs(dirPth + PthSep + fi.Name())
		} else {

			files = append(files, dirPth+PthSep+fi.Name())
		}
	}

	return files, dirs, nil
}
func getSvnInfo(path string)(str string)  {
	cmd := exec.Command("svn", "info",path)
	out, err := cmd.CombinedOutput()
	
	cmd.Run()
	if err != nil{
		fmt.Printf("combined out:\n\n",err)
	}
	output := mahonia.NewDecoder("gbk").ConvertString(string((out)))
	version := matchVersion(output)

	return version
}

func matchVersion(output string)(version string)  {
	reg1 := regexp.MustCompile(`(最后修改的版本:|Last Changed Rev:)\s(?s:(.*?))\s\s(最后修改的时间|Last Changed Date)`)
	if reg1 == nil { //解释失败,返回nil
		fmt.Println("regexp err")
		return "noVersion"
	}
	params := reg1.FindStringSubmatch(output)

	//result1 := reg1.FindAllStringSubmatch(output, -1)
	//fmt.Println("result1 = ", result1,params[2])
	return params[2]
}

重点看下获取svn信息那里就行了,发送文件的代码在网上能找到,提醒一下只装了TortoiseSVN是不行的,还要装一下sliksvn才能执行svn指令,如果dos窗口执行不了svn的话百度一下就行。

server

package main

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"database/sql"
	"fmt"
	"github.com/jthmath/winapi"
	_ "github.com/mattn/go-sqlite3"
	lua "github.com/yuin/gopher-lua"
	"github.com/yuin/gopher-lua/parse"
	"html/template"
	"io"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var insertStmt *sql.Stmt = nil
var udpateStmt *sql.Stmt = nil
var udpateInfoStmt *sql.Stmt = nil
var removeStmt *sql.Stmt =nil

var dbServe *sql.DB = nil

func main()  {
	listener,err:= net.Listen("tcp","127.0.0.1:9090")
	if err != nil {
		fmt.Println("listen fail err=",err)
		return
	}
	defer listener.Close()

	initDB()
	for {
		conn,err1 := listener.Accept()

		if err1 != nil{
			fmt.Println("listen accept err=",err)
			break
		}

		go recvFile(conn)
	}

	dbServe.Close()
}
func recvFile(conn net.Conn)  {
	defer conn.Close()
	//读取客户端请求
	buf1:= make([]byte,1024)
	var n int
	n,err := conn.Read(buf1)
	if err != nil{
		fmt.Println("conn read err=",err)
		return
	}
	fileName := string(buf1[:n])

	//stringTable := bytes.Split(buf1,[]byte("|"))
	stringTable := strings.Split(fileName,"|")
	s1 := string(stringTable[1])
	s0 := string(stringTable[0])
	s2 := string(stringTable[2])
	tempPath := s0

	tempPath = string(s0[13:len(s0)])
	uiName:=matchFileName(s0)

	//文件夹类型
	if s1 == "2"{
		pathTable := strings.Split("发过来的文件存放的目录",string(os.PathSeparator))
		createFile(pathTable)
	}
	conn.Write([]byte("ok"))

	//新建文件
	f,err := os.Create("发过来的文件存放的目录 跟上面是同一个")
	if err != nil {
		fmt.Println("create file fail",err)
		return
	}
	defer f.Close()
	//从 conn读取本地文件
	buf:= make([]byte,0)
	tmp := make([]byte, 256)
	//finnalBuf :=make([]byte,4096)

	for {
		n,err := conn.Read(tmp)
		buf = append(buf, tmp[:n]...)

		if err != nil && err != io.EOF{
			fmt.Println("conn.read err=",err)
		}
		if n == 0 {
			zliBytes := DoZlibCompress(buf)
			SaveToDataBase(s2,uiName,zliBytes)
			fmt.Println("文件接受完毕")
			return
		}
		f.Write(tmp[:n])
	}
}

func SaveToDataBase(s2 string,uiName string,viewInfo []byte)  {
	version, err := strconv.Atoi(s2)
	if err == nil {
		lastVersion := findVersionInfo(uiName)
		if lastVersion != -1{
			if TotalCount == 0 {
				TotalCount = GetSqlCount()
			}
			if version > lastVersion{
				fmt.Println("当前版本号大于数据库储存版本号,更新数据库")
				UpdateInfo(uiName,version)
				UpdateViewInfo(uiName,viewInfo)
			}else if lastVersion > version {
				if viewInfo != nil{
					fmt.Println("当前版本号小于数据库储存版本号,更新文件",lastVersion,version,uiName)
				}

			}
		}else {
			InsertInfo(uiName,version,viewInfo)
		}
	}
}

func isFileExists(path string)(bool,error)  {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func createFile(pathTable []string)  {
	curDir := ""
	for index,Path := range pathTable {
		if index != len(pathTable) - 1{
			curDir = curDir+Path+string(os.PathSeparator)
		}
	}
	isExist,exitError := isFileExists(curDir)
	if exitError != nil{
		fmt.Println("获取文件错误")
	}else{
		if isExist{
			fmt.Println("存在",curDir)
		}else{
			err := os.MkdirAll( curDir , 0777)
			if err != nil {
				fmt.Println("错误",err)
				return
			}
			fmt.Println("创建",curDir)
		}
	}
}

func initDB ()  {
	db, err := sql.Open("sqlite3", "./data.db")
	//db.Exec("PRAGMA journal_mode=WAL;")
	db.SetMaxOpenConns(1)
	checkErr(err)

	fileInfoTable := `CREATE TABLE IF NOT EXISTS fileInfo(
		fileName TEXT PRIMARY KEY,
		version INT NOT NULL,
		viewInfo BLOB NULL)`
	db.Exec(fileInfoTable)
	// 插入数据
	insertStmt, err = db.Prepare("INSERT OR IGNORE INTO fileInfo(fileName, version,viewInfo) values(?,?,?)")

	checkErr(err)

	// 更新数据
	udpateStmt, err = db.Prepare("UPDATE fileInfo set version=? where fileName=?")
	checkErr(err)
	// 更新数据
	udpateInfoStmt, err = db.Prepare("UPDATE fileInfo set viewInfo=? where fileName=?")
	checkErr(err)
	//删除数据
	removeStmt,err = db.Prepare("DELETE FROM fileInfo where fileName=?")
	checkErr(err)

	dbServe = db

}

func InsertInfo(fileName string,version int,viewInfo []byte)  {
	//tx, err := dbServe.Begin()//打开事物
	//defer tx.Commit()//事物提交
	res, err := insertStmt.Exec(fileName,version,viewInfo)
	affect, err := res.RowsAffected()
	fmt.Println("affect====",affect)
	checkErr(err)
}

func UpdateInfo(fileName string,version int)  {
	res, err := udpateStmt.Exec(version,fileName)
	affect, err := res.RowsAffected()
	fmt.Println("affect====",affect)
	checkErr(err)
}

func UpdateViewInfo(fileName string,viewInfo []byte)  {
	//tx, err := dbServe.Begin()//打开事物
	//defer tx.Commit()//事物提交
	res, err := udpateInfoStmt.Exec(viewInfo,fileName)
	affect, err := res.RowsAffected()
	fmt.Println("affect====",affect)
	checkErr(err)
}

func findVersionInfo(name string)(v int)  {
	//tx, err := dbServe.Begin()//打开事物
	//defer tx.Commit()//事物提交
	// 查询数据
	rows, err := dbServe.Query("SELECT version FROM fileInfo where fileName = ?",name)
	checkErr(err)
	var version int = -1
	defer rows.Close()
	for rows.Next() {

		err = rows.Scan(&version)
		checkErr(err)
	}

	return version
}

func findViewInfo(name string)(v []byte)  {
	//tx, err := dbServe.Begin()//打开事物
	//defer tx.Commit()//事物提交
	// 查询数据
	rows, err := dbServe.Query("SELECT viewInfo FROM fileInfo where fileName = ?",name)

	checkErr(err)
	var view []byte = nil
	defer rows.Close()
	for rows.Next() {
		err = rows.Scan(&view)
		checkErr(err)
	}

	return view
}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
}

func matchFileName(str string)(fileName string)  {
	pathTable := strings.Split(str,string(os.PathSeparator))
	return pathTable[len(pathTable) - 1]
}

//进行zlib压缩
func DoZlibCompress(src []byte) []byte {
	var in bytes.Buffer
	w := zlib.NewWriter(&in)
	w.Write(src)
	w.Close()
	return in.Bytes()
}

//进行zlib解压缩
func DoZlibUnCompress(compressSrc []byte) []byte {
	b := bytes.NewReader(compressSrc)
	var out bytes.Buffer
	r, _ := zlib.NewReader(b)
	io.Copy(&out, r)
	return out.Bytes()
}

server是接受文件和存进数据库的,也有几个坑,go协程写入文件信息会报错,印象中查的原因是说协程db有可能边插入update,导致状态冲突锁住,设置db.SetMaxOpenConns(1)解决;第二个是写入文件信息初始化buf:= make([]byte,0)切片大小要为0,不然设置了大小写完后前面会有一大堆空格。如果以上代码不能直接运行的话留意一下目录设置那里,那里的处理是按我本地文件路径设置的。
以上就是这次要分享的,最后想说的是这次弄这个东西查资料的时候发现资料很少,很多说得很简单的,要不就是锁着要付费,然后就感觉很不爽,所以更加要分享出来。程序员本来就很苦逼,如果不是互联网牛逼可以查到很多信息会更苦逼,你当初菜的时候去学人家的,现在会了就藏着掖着,真是狗东西。
真的鼓励各位技术大佬学新东西的时候把遇到的问题大方分享出来,除了能回顾,还能记录,甚至还能帮到有问题的人,这种事要多做啊。


版权声明:本文为x2345com原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。