注册 登录
  • 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

Golang:gosync 简单文件同步 Simple File Sync

go 水墨上仙 2645次浏览 已收录 手机上查看

前言:最近学习Go语言 尝试写了一个简单的文件同步,基于tcp的,功能比较简单。基于Go version 1 编译。
该程序的功能:

只能传输单一文件,文件名不能有特殊符号和空格
有serve端和client命令 使用同一个程序
不能同步目录结构
不能改名字
无法查看进度
该程序主要就是将指定的文件同步到新的服务器和指定的目录下面,并且该文件的时间和属性跟原来一样,可以使用rsync进行检测。
该程序名字 gosync
gosync.go
转自:http://www.ohlinux.com/archives/815/

package main
 
import (
    "os"
    "fmt"
    "net"
    "flag"
    "time"
    "crypto/md5"
    "io"
    "strings"
    "strconv"
)
 
type sysFileInfo struct{
    fName       string
    fSize       int64
    fMtime      time.Time
    fPerm       os.FileMode
    fMd5        string
    fType       bool
}
 
var (
    listenPort = flag.String( "port","7722","server listen port" )
    syncFile = flag.String( "file","","transfer file" )
    syncHost = flag.String( "host","","server host" )
    syncSer = flag.Bool( "d",false,"server mode")
    syncFold = flag.String( "dir","/tmp/gosync/","recive sync fold ")
)
 
func main(){
    flag.Parse()
    if *syncSer {  
        servPort:=fmt.Sprintf( ":%s",*listenPort )
        l,err := net.Listen( "tcp",servPort )
        if err != nil{
           fmt.Println( "net failed",err ) 
        }
        err = os.MkdirAll( *syncFold , 0755)
        if err != nil{
           fmt.Println( err ) 
        }
        fmt.Println( "Start Service" )
        Serve( l )
     }else{  
        destination:=fmt.Sprintf( "%s:%s",*syncHost,*listenPort )
        clientSend( *syncFile,destination)
     }
}
 
func clientSend(files string,destination string){
    fInfo:=getFileInfo( files)
    newName :=fmt.Sprintf( "%s",fInfo.fName)
    cmdLine:=  fmt.Sprintf( "upload %s %d %d %d %s " ,newName,fInfo.fMtime.Unix(),fInfo.fPerm,fInfo.fSize,fInfo.fMd5) 
    cn,err:=net.Dial( "tcp", destination)
    if err !=nil {
        fmt.Println( "connect error",err )
        return
    }
    defer cn.Close()
    cn.Write( []byte( cmdLine ) )
    cn.Write( []byte( "\r\n" ) )
    fileHandle,err := os.Open( files )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return
    }
    io.Copy( cn,fileHandle)
    for{
        buffer :=make( []byte,1024)
        num,err := cn.Read(buffer)
        if err == nil && num > 0{
            fmt.Println(  string(buffer[ :num ]) )
            break
        }
    }
}
 
func getFileInfo( filename string) *sysFileInfo{
    fi,err:= os.Lstat( filename ) 
    if err != nil {
        fmt.Println("info ERROR",err) 
        return nil
    }
    fileHandle,err := os.Open( filename )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return nil
    }
 
    h := md5.New()
    _,err = io.Copy( h,fileHandle )
    fileInfo := & sysFileInfo {
        fName : fi.Name(),
        fSize : fi.Size(),
        fPerm : fi.Mode().Perm(),
        fMtime: fi.ModTime(),
        fType : fi.IsDir(),
        fMd5  : fmt.Sprintf( "%x", h.Sum( nil )),
    }
        return fileInfo
}
 
func Serve( l net.Listener) {
    for{
        conn,err := l.Accept()
        if err != nil{
            if ne,ok := err.( net.Error );ok && ne.Temporary(){
                continue
            }
            fmt.Println( "network error",err )
        }
        go Handler(conn)
    }
}
 
func Handler( conn net.Conn) {
    defer conn.Close()
    state := 0
    var cmd *sysFileInfo
    var fSize int64
    var tempFileName string
    var n int64
    for {
        buffer :=make( []byte,2048)
        num,err := conn.Read(buffer)
        numLen:=int64( num )
        if err != nil && err != io.EOF {
            fmt.Println( "cannot read",err )
        }
        n=0
        if state  == 0 {
            n,cmd = cmdParse( buffer[:num] )
            tempFileName = fmt.Sprintf( "%s.newsync",cmd.fName)
            fSize = cmd.fSize
            state = 1
        } 
        if state == 1 {
            last := numLen
            if fSize <= numLen-n {
                last = fSize + n
                state = 2
            }
            err = writeToFile( buffer[int( n ):int( last )],tempFileName,cmd.fPerm )
            if err != nil{
                fmt.Println( "read num error : ",err )
            }
            fSize -=last-n 
            if state == 2{
                os.Remove( cmd.fName)
                err = os.Rename( tempFileName,cmd.fName)
                if err != nil{
                    fmt.Println( "rename ",tempFileName," to ",cmd.fName," failed" ) 
                }
                err = os.Chtimes( cmd.fName,time.Now(),cmd.fMtime )
                if err != nil{
                    fmt.Println( "change the mtime error ",err ) 
                }
                fileHandle,err := os.Open( cmd.fName)
                if err != nil {
                    fmt.Println("open ERROR",err) 
                }
                h := md5.New()
                io.Copy( h,fileHandle )
                newfMd5 := fmt.Sprintf( "%x", h.Sum( nil ))
                if newfMd5 == cmd.fMd5{
                    sendInfo:=fmt.Sprintf("%s sync success",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }else{
                    sendInfo:=fmt.Sprintf("%s sync failed",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }
            }
        }
    }
}
 
func cmdParse( infor []byte) ( int64 , *sysFileInfo) {
    var i int64
    for i=0;i<int64(len(infor));i++ {
       if infor[i] == '\n' && infor[i-1]  == '\r' {
           cmdLine:=strings.Split( string( infor[:i-1] ) ," ") 
           fileName := fmt.Sprintf( "%s/%s",*syncFold,cmdLine[ 1 ] )
           filePerm, _ := strconv.Atoi( cmdLine[ 3 ])
           fileMtime,_:= strconv.ParseInt( cmdLine[ 2 ],10,64 )
           fileSize,_:= strconv.ParseInt( cmdLine[ 4 ],10,64)
           fileInfo := & sysFileInfo {
                fName : fileName, 
                fMtime: time.Unix( fileMtime,0 ),
                fPerm : os.FileMode(filePerm),
                fSize : fileSize,
                fMd5  : string(cmdLine[ 5 ]),
           }
           return i+1,fileInfo
       }
    }
       return 0,nil
}
 
func writeToFile( data []byte ,fileName string,perm os.FileMode)  error{
    writeFile,err := os.OpenFile( fileName,os.O_RDWR | os.O_APPEND | os.O_CREATE ,perm)
    if err != nil{
        fmt.Println( "write file error:",err )
        return err
    }
    defer writeFile.Close()
    _,err = writeFile.Write( data )
    if err != nil{
       fmt.Println( "write file error",err ) 
       return err 
    }
    return nil
}

使用

Usage of ./gosync:
  -d=false: server mode
  -dir="/tmp/gosync/": recive sync fold 
  -file="": transfer file
  -host="": server host
  -port="7722": server listen port

服务端开启

#./gosync&nbsp-d&nbsp-dir&nbsp/tmp/xxxx

client传输文件:

# ./gosync --file=/root/Videos/DSCF2394.avi --host=127.0.0.1 --port=7722
/tmp/xxxx/DSCF2394.avi sync success

手动检查文件正确性:

# md5sum /tmp/xxxx/DSCF2394.avi 
eb50332d3b3b6f36b773046aca16e908  /tmp/xxxx/DSCF2394.avi
# md5sum /root/Videos/DSCF2394.avi
eb50332d3b3b6f36b773046aca16e908  /root/Videos/DSCF2394.avi


喜欢 (0)
[开心洋葱]
分享 (0)
关于作者:
水墨上仙
……
加载中……