最新的go 上传ftp脚本2-3分钟上传1万多文件

package main

import (
    "io"
    "os"
    "fmt"
    "net"
    "sync"
    "path"
    "time"
    // "math"
    "errors"
    "bytes"
    "os/exec"
    "strconv"
    "strings"
    "runtime"
    "io/ioutil"
    "encoding/json"
    "path/filepath"
)

type FTP struct {
    host     string
    port     int
    user     string
    passwd   string
    pasv     int
    cmd      string
    Code     int
    Message  string
    Debug    bool
    stream   []byte
    conn     net.Conn
    dataConn net.Conn
    // Error os.Error
}

type Config struct {
    Host      string // ip
    Port      int    // 端口
    User      string // 用户名
    Passwd    string // 密码
    LocalDir  string // 本地位置
    LocalFile string // 本地文件
    FtpDir    string // ftp位置
    CpuDouble int    // cpu倍数
    Debug     bool
}

var config Config
var zipModeSize = 1024 // 压缩模式大小
var fileBuffer bytes.Buffer
var waitgroup sync.WaitGroup  // 记录进程处理结束
var cpu_double = 3             // cpu核心倍数
var ftpTatleNum = 0         // 上传总数
var versionInit = 0            // 初始版本
var FormatDate = "2006-01-02 15:04:05"
var resourceDirPath = ""
var resourceFtpDir = ""


func main() {
    get_min_version()
    fmt.Println("初始版本: ", versionInit)
    var calcTime = time.Now().Unix()
    var param = "qq"
    var ftp = new(FTP)
    paramLen := len(os.Args)
    if paramLen == 2 {
        param = os.Args[1]
    }
      _, currFilename, _, _ := runtime.Caller(0)
      currFilename = get_file_name(currFilename)
    flaConfigPath := currFilename + "_" + param + "_config.json"
    if !checkFileIsExist(flaConfigPath) {
        fmt.Println("当前目录缺少文件: ", flaConfigPath)
        return
    }
    readConfig(flaConfigPath)
    if config.CpuDouble > 0 {
        cpu_double = config.CpuDouble
    }
    ftp.Debug = config.Debug

    ftp.svn_up_dir(config.LocalDir)
    // connect
    ftp.Connect(config.Host, config.Port)
    // login
    ftp.Login(config.User, config.Passwd)
    // login failure
    if ftp.Code == 530 {
        fmt.Println("error: login failure")
        os.Exit(-1)
    }
    // pwd
    ftp.Pwd()
    fmt.Println("code:", ftp.Code, ", message:", ftp.Message)

    resourceFtpDir = config.FtpDir + "resource/"
    ftp.Mkd(resourceFtpDir)
    FilelistDir := resourceFtpDir + "filelist/"
    ftp.Mkd(FilelistDir)

    resourceDirPath  = config.LocalDir + "resource/"
    filelistDirPath := resourceDirPath + "filelist/"

    ftp.dir_request(resourceDirPath, filelistDirPath, resourceFtpDir, FilelistDir)
    // quit
    ftp.Quit()

    TimeStr := strconv.FormatInt(time.Now().Unix() - calcTime, 10)         // int64转成string 
    logStr := "上传文件数 >> " + strconv.Itoa(ftpTatleNum) + "个\t  总共用时 >> "+ TimeStr + " 秒"
    logFileWrite(logStr + "\r\n")
    fmt.Println(logStr)
    // fmt.Println(" 总共用时 >> ", toTime - calcTime, " 秒")
}

// 更新目录内容
func (ftp *FTP) svn_up_dir(pathDir string) {
    cmd := exec.Command("/bin/sh", "-c", `svn up`)
    cmd.Dir = string_remove(pathDir)
    out, err := cmd.CombinedOutput()
    if err != nil {
        fmt.Println(err, "svn_up_dir err: /n", string(out))
        os.Exit(3)
    }
    ftp.debugInfo("svn_up " + pathDir)
}

func (ftp *FTP) dir_request(resourceDirPath string, filelistDirPath string, ftpDirPath string, ftpFilelistDir string) {
    fmt.Println("文件夹内容 ==>> ", resourceDirPath)
    runtime.GOMAXPROCS(runtime.NumCPU())
    resourceDirs, _ := ioutil.ReadDir(resourceDirPath)
    for _, resourceDir := range resourceDirs {
        if resourceDir.IsDir() {
            fileName := resourceDir.Name()
            v, _ := strconv.Atoi(fileName) 
            if v == 0 || versionInit > v && versionInit > 0  {
                continue
            }
            ftpVresionDir := ftpDirPath + fileName + "/"
            LocalDir := resourceDirPath + fileName + "/"
            ftp.Mkd(ftpVresionDir)
            if 
                ftp.Code == 257 {
                      ftp.ftp_request(LocalDir, "", ftpVresionDir)
                    ftp.ftp_request(filelistDirPath, fileName+".json", ftpFilelistDir)
                    ftp_buff()
                    fileBuffer.Reset()
            }
            logFileWrite("res:\t"+ fileName)
        } else {
            continue
        }
    }
}


func (ftp *FTP) ftp_request(dirPath string, fileName string, ftpDirPath string) {
    runtime.GOMAXPROCS(runtime.NumCPU())
    dirPath = strings.Replace(dirPath, "\\", "/", -1)
    toDirPath := dirPath + fileName
    fmt.Println("开始上传文件夹内容 ==>> ", toDirPath)
    var dirTotleNum = 0
    var fileTotleNum = 0
    var pathStrRecord = ""
    filepath.Walk(toDirPath, func(path1 string, file os.FileInfo, err error) error {
        if file == nil {return err}
        if strings.Index(path1, ".svn") != -1 {return nil} // 过滤svn文件
        localPath := strings.Replace(path1, "\\", "/", -1)
        if strings.Index(path1, "+") != -1 {
            fmt.Println("ftp 上传文件错误: ", localPath)
            logFileWrite("上传文件错误:\t"+localPath)
            return nil
        } // 过滤无法上传文件
        ftpPath := strings.Replace(localPath, dirPath, ftpDirPath, -1)
        if file.IsDir() {
            dirTotleNum ++
            ftp.Mkd(ftpPath)
            return nil
        }
        fileTotleNum ++
        if fileName != "" {
            // ftp.Request("TYPE I")
            // byteFile, _ := ioutil.ReadFile(localPath)
            // ftp.Stor(ftpPath, byteFile)
            ftp.PutPasv(localPath, ftpPath)
        } else if pathStrRecord == "" {
            pathStrRecord = localPath + "," + ftpPath
            fileBuffer.WriteString(pathStrRecord)
        } else {
            fileBuffer.WriteString("\r\n" + localPath + "," + ftpPath)
        }
        // fileBuffer.WriteString(path1 + "," + path2 + "\r\n")
        return nil
    })

    ftpTatleNum += dirTotleNum
    // waitgroup.Wait() //Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
    fmt.Println("上传文件夹数量 ==>>", dirTotleNum, "\t 要上传文件 ==>>", fileTotleNum)
}

func ftp_buff() {
    var    calcTime = time.Now().Unix()
    filePathStr := fileBuffer.String()
    if filePathStr == "" {
        return
    }
    ftpStrList := strings.Split(filePathStr, "\r\n")
    Len := len(ftpStrList)
    LoopNum := runtime.NumCPU() * cpu_double
    if LoopNum > Len {
        LoopNum = Len
    }

    typeChan := make(chan int, LoopNum)
    var ii = 0
    var tatalNum = 0
    for i := 1; i <= LoopNum; i++ {
        ii = i - 1
        tatalNum ++
        go request_one(i, ftpStrList[ii], ii, typeChan)
    }
      fmt.Println("LoopNum ", LoopNum)
    for {
          select {
        case type1 := <-typeChan:
            ii ++ 
            if ii >= Len {
                LoopNum -- 
                  fmt.Println("LoopNum >>>", LoopNum, ii)
                // continue
            }else{
                tatalNum ++
                  go request_one(type1, ftpStrList[ii], Len-ii, typeChan)
            }
          }
        if LoopNum == 0 {
              fmt.Println("for LoopNum", LoopNum)
            break
        }
      }
    var toTime = time.Now().Unix()
    ftpTatleNum += tatalNum
    fmt.Println("文件总共:", Len, "总处理文件数:", tatalNum , " 总共用时 >> ", toTime - calcTime, " 秒", runtime.NumGoroutine())
    // fmt.Println(" 总共用时 >> ", toTime - calcTime, " 秒")
}

func request_one(typeChange int, pathStr string, orderId int,typeChan chan <- int) {
    if pathStr == "" {
        typeChan <- typeChange
        return
    }
    var ftp = new(FTP)
    ftp.Debug = config.Debug
    // connect
    ftp.Connect(config.Host, config.Port)
    // login
    ftp.Login(config.User, config.Passwd)
    // login failure
    if ftp.Code == 530 {
        fmt.Println("error: login failure")
        typeChan <- typeChange
        os.Exit(-1)
    }
    // pwd
    ftp.Pwd()
    pathStrList := strings.Split(pathStr, ",")
    if len(pathStrList) < 2 {
        fmt.Println("\t:len(pathStrList) < 2 -------- 进程 ",pathStr)
        typeChan <- typeChange
        return
    }
    path1 := pathStrList[0]
    path2 := pathStrList[1]
    fmt.Println(orderId, "\t:", path1)
    ftp.PutPasv(path1, path2)
    ftp.Quit()
    typeChan <- typeChange
}

// 获得初始版本
func get_min_version() {
    var versionPath = "version.txt"     // 版本文件名字
    if checkFileIsExist("../"+versionPath) {
        versionPath = "../"+versionPath
    }
    if checkFileIsExist(versionPath) {
        versionBytes, _ := ioutil.ReadFile(versionPath)
        versionLines := strings.Split(string(versionBytes), "\r\n")
        var currDate1 = ""
        for _, versionValue := range versionLines {
            if "" != versionValue {
                line := strings.Split(versionValue, "\t")
                currDate1 = line[2]
            }
        }
        if currDate1 != "" {
            currDate2 := strings.Split(currDate1, ":")
            v, _ := strconv.Atoi(currDate2[1]) 
            if v == 0 {
                return
            }
            versionInit = v
        }
    }
}


func (ftp *FTP) Connect(host string, port int) {
    addr := fmt.Sprintf("%s:%d", host, port)
    ftp.conn, _ = net.Dial("tcp", addr)
    ftp.Response()
    ftp.host = host
    ftp.port = port
}

func (ftp *FTP) Login(user, passwd string) {
    ftp.Request("USER " + user)
    ftp.Request("PASS " + passwd)
    ftp.user = user
    ftp.passwd = passwd
    if ftp.Code == 230 {
        return
    }
    fmt.Println("登录失败:",ftp.Message)
    os.Exit(-1)
    return
}

func (ftp *FTP) Response() (code int, message string) {
    ret := make([]byte, 1024)
    n, _ := ftp.conn.Read(ret)
    msg := string(ret[:n])
    if len(msg) <= 3 {
        str := strings.Replace(ftp.cmd, resourceFtpDir, resourceDirPath, -1)
        fmt.Println("ftp 上传文件错误: ", str)
        logFileWrite("上传文件错误:\t" + str)
        time.Sleep(1 * time.Second)
        return 450, ""
    }
    code, _ = strconv.Atoi(msg[:3])
    message = msg[4 : len(msg)-2]
    ftp.debugInfo("<*cmd*> " + ftp.cmd)
    ftp.debugInfo(fmt.Sprintf("<*code*> %d", code))
    ftp.debugInfo("<*message*> " + message)
    return
}

func (ftp *FTP) Request(cmd string) {
    ftp.conn.Write([]byte(cmd + "\r\n"))
    ftp.cmd = cmd
    ftp.Code, ftp.Message = ftp.Response()
}


func (ftp *FTP) Pasv() {
    ftp.Request("PASV")
}

func (ftp *FTP) Pwd() {
    ftp.Request("PWD")
}

func (ftp *FTP) Cwd(path string) {
    ftp.Request("CWD " + path)
}

func (ftp *FTP) Mkd(path string) {
    ftp.Request("MKD " + path)
}

func (ftp *FTP) Size(path string) (size int) {
    ftp.Request("SIZE " + path)
    size, _ = strconv.Atoi(ftp.Message)
    return
}

func (ftp *FTP) List() {
    ftp.Pasv()
    ftp.Request("LIST")
}

func (ftp *FTP) Quit() {
    ftp.Request("QUIT")
    ftp.conn.Close()
}


func (ftp *FTP) PutPasv(localPath,Pathname string) {
    ftp.Request("TYPE I")
    ftp.Pasv()
    con, err := ftp.newRequest("STOR", Pathname)
    if err != nil {
        return
    }
    File, err := os.Open(localPath)
    if err != nil {
        con.Close()
        return
    }
    io.Copy(con, File)
    File.Close()
    con.Close()
    ftp.Response()
}

func (ftp *FTP) newRequest(cmd, Pathname string) (net.Conn, error) {
    ftp.getport()
    con, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ftp.host, ftp.pasv))
    if err != nil {
        return nil, err
    }
    ftp.Request(fmt.Sprintf("%s %s", cmd, Pathname))
    if ftp.Code != 150 && ftp.Code != 125{
        fmt.Println("ftp 数据失败", ftp.Code)
        con.Close()
        return nil, errors.New("create data link error.")
    }
    return con, nil
}
func (ftp *FTP) getport() {
    start, end := strings.Index(ftp.Message, "("), strings.Index(ftp.Message, ")")
    s := strings.Split(ftp.Message[start:end], ",")
    l1, _ := strconv.Atoi(s[len(s)-2])
    l2, _ := strconv.Atoi(s[len(s)-1])
    ftp.pasv = l1*256 + l2
}

// 读取配置
func readConfig(path string) {
    bytes, _ := ioutil.ReadFile(path)
    err := json.Unmarshal(bytes, &config)
    if err != nil {
        fmt.Println("error in translating,", err.Error())
        return
    }
}

func (ftp *FTP) debugInfo(s string) {
    if ftp.Debug {
        fmt.Println(s)
    }
}

// 写入日志文件
func logFileWrite(content string){
    logName := "log_ftp.txt"
    fileObj, err := os.OpenFile(logName, os.O_CREATE|os.O_RDWR|os.O_WRONLY|os.O_APPEND, 0766)
    if err != nil {
        fmt.Println("Failed to open the file",err.Error())
        os.Exit(2)
    }
    logTime:=time.Now().Format(FormatDate)
    contentText := "\r\n"
    if content != "" {
        contentText = logTime + "\t" + content + "\r\n"
    }
    if _,err := fileObj.WriteString(contentText);err != nil {
        fmt.Println("写入日志文件错 err",content)
    }
    defer fileObj.Close()
}


// 获得文件名字
func get_file_name(Path string) string {
    fileName := path.Base(Path) //获取文件名带后缀
    fileNameSuffix := path.Ext(fileName)                //获取文件后缀
    return strings.TrimSuffix(fileName, fileNameSuffix) // 获取路径和文件名
}

/**
 * 判断文件是否存在  存在返回 true 不存在返回false
 */
func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        exist = false
    }
    return exist
}

// 获得文件夹路径        去除路径文件部分
func string_remove(str string) (file string) {
    return path.Dir(str)
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值