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)
}