diff --git a/Makefile b/Makefile index 93e8777..72b36e4 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ PROJ_DIR= $(ROOT)/src/upyun.com PWD= $(shell pwd) app: - go build -o $(APP) . + CGO_ENABLED=0 go build -o $(APP) . test: go test -v . diff --git a/commands.go b/commands.go index 733142d..fd75de4 100644 --- a/commands.go +++ b/commands.go @@ -306,16 +306,46 @@ func NewPutCommand() cli.Command { InitAndCheck(LOGIN, CHECK, c) localPath := c.Args().First() upPath := "./" + + if c.NArg() > 2 { + fmt.Println("Use the upload command instead of the put command for multiple file uploads") + os.Exit(0) + } + if c.NArg() > 1 { upPath = c.Args().Get(1) } - session.Put(localPath, upPath, c.Int("w")) + session.Put( + localPath, + upPath, + c.Int("w"), + ) + return nil + }, + Flags: []cli.Flag{ + cli.IntFlag{Name: "w", Usage: "max concurrent threads", Value: 5}, + }, + } +} + +func NewUploadCommand() cli.Command { + return cli.Command{ + Name: "upload", + Usage: "upload multiple directory or file", + Action: func(c *cli.Context) error { + InitAndCheck(LOGIN, CHECK, c) + session.Upload( + c.Args().Tail(), + c.String("remote"), + c.Int("w"), + ) return nil }, Flags: []cli.Flag{ cli.IntFlag{Name: "w", Usage: "max concurrent threads", Value: 5}, + cli.StringFlag{Name: "remote", Usage: "remote path", Value: "./"}, }, } } diff --git a/session.go b/session.go index 84cbf0a..dec1bc7 100644 --- a/session.go +++ b/session.go @@ -58,6 +58,13 @@ type delTask struct { isdir bool } +type UploadedFile struct { + barId int + LocalPath string + UpPath string + LocalInfo os.FileInfo +} + var ( session *Session ) @@ -521,12 +528,42 @@ func (sess *Session) putFileWithProgress(barId int, localPath, upPath string, lo return idx, err } +func (sess *Session) putFilesWitchProgress(localFiles []*UploadedFile, workers int) { + var wg sync.WaitGroup + + tasks := make(chan *UploadedFile, workers*2) + for w := 0; w < workers; w++ { + wg.Add(1) + go func() { + defer wg.Done() + for task := range tasks { + _, err := sess.putFileWithProgress( + task.barId, + task.LocalPath, + task.UpPath, + task.LocalInfo, + ) + if err != nil { + fmt.Println("putFileWithProgress error: ", err.Error()) + return + } + } + }() + } + + for _, f := range localFiles { + tasks <- f + } + + close(tasks) + wg.Wait() +} + func (sess *Session) putDir(localPath, upPath string, workers int) { type FileInfo struct { fpath string fInfo os.FileInfo } - localFiles := make(chan *FileInfo, workers*2) var wg sync.WaitGroup var err error @@ -563,6 +600,7 @@ func (sess *Session) putDir(localPath, upPath string, workers int) { wg.Wait() } +// Put 上传单文件或单目录 func (sess *Session) Put(localPath, upPath string, workers int) { upPath = sess.AbsPath(upPath) localInfo, err := os.Stat(localPath) @@ -596,6 +634,59 @@ func (sess *Session) Put(localPath, upPath string, workers int) { } } +// Copy put的升级版命令 +func (sess *Session) Upload(filenames []string, upPath string, workers int) { + upPath = sess.AbsPath(upPath) + + // 检测云端的目的地目录 + upPathExist, upPathIsDir := false, false + if upInfo, _ := sess.updriver.GetInfo(upPath); upInfo != nil { + upPathExist = true + upPathIsDir = upInfo.IsDir + } + // 多文件上传 upPath 如果存在则只能是目录 + if upPathExist && !upPathIsDir { + PrintErrorAndExit("upload: %s: Not a directory", upPath) + } + + var ( + files []string + dirs []string + ) + for _, filename := range filenames { + localInfo, err := os.Stat(filename) + if err != nil { + PrintErrorAndExit(err.Error()) + } + if localInfo.IsDir() { + dirs = append(dirs, filename) + } else { + files = append(files, filename) + } + } + + // 上传目录 + for _, localPath := range dirs { + sess.putDir(localPath, path.Join(upPath, filepath.Base(localPath)), workers) + } + + // 上传文件 + var uploadedFile []*UploadedFile + for _, localPath := range files { + localInfo, err := os.Stat(localPath) + if err != nil { + fmt.Printf("stat %s: %v\n", localPath, err) + } + uploadedFile = append(uploadedFile, &UploadedFile{ + barId: -1, + LocalPath: localPath, + UpPath: path.Join(upPath, filepath.Base(localPath)), + LocalInfo: localInfo, + }) + } + sess.putFilesWitchProgress(uploadedFile, workers) +} + func (sess *Session) rm(fpath string, isAsync bool, isFolder bool) { err := sess.updriver.Delete(&upyun.DeleteObjectConfig{ Path: fpath, diff --git a/upx.go b/upx.go index 10e0721..9e89f39 100644 --- a/upx.go +++ b/upx.go @@ -54,6 +54,7 @@ func main() { NewTreeCommand(), NewGetCommand(), NewPutCommand(), + NewUploadCommand(), NewRmCommand(), NewSyncCommand(), NewAuthCommand(),