github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/third/kmgQiniu/upload.go (about) 1 package kmgQiniu 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/bronze1man/kmg/kmgTask" 9 qiniuIo "github.com/qiniu/api/io" 10 "github.com/qiniu/api/rs" 11 "github.com/qiniu/rpc" 12 ) 13 14 //上传文件或目录 15 var Upload = UploadDirMulitThread 16 17 //单线程上传目录 18 //检查同文件名和同内容的文件是否存在,如果内容相同便不上传 19 func UploadDir(ctx *Context, localRoot string, remoteRoot string) (err error) { 20 return filepath.Walk(localRoot, func(path string, info os.FileInfo, inErr error) (err error) { 21 if inErr != nil { 22 return inErr 23 } 24 if info.IsDir() { 25 return 26 } 27 relPath, err := filepath.Rel(localRoot, path) 28 if err != nil { 29 return fmt.Errorf("[qiniuUploadDir] filepath.Rel err:%s", err.Error()) 30 } 31 remotePath := filepath.Join(remoteRoot, relPath) 32 err = UploadFileCheckExist(ctx, path, remotePath) 33 if err != nil { 34 return fmt.Errorf("[qiniuUploadDir] qiniuUploadFile err:%s", err.Error()) 35 } 36 return 37 }) 38 } 39 40 type uploadFileRequest struct { 41 localPath string 42 remotePath string 43 expectHash string 44 } 45 46 //多线程上传目录 47 //1.某个文件仅在一个线程中上传, 48 //2.检查同名和同内容的文件是否已经存在了,如果存在,且hash相同便不上传(断点续传) 49 // TODO 解决一边上传,一边修改的bug. 50 func UploadDirMulitThread(ctx *Context, localRoot string, remoteRoot string) (err error) { 51 tm := kmgTask.NewLimitThreadErrorHandleTaskManager(ThreadNum, 3) 52 defer tm.Close() 53 requestList := []uploadFileRequest{} 54 remotePathList := []string{} 55 //dispatch task 分配任务 56 err = filepath.Walk(localRoot, func(path string, info os.FileInfo, inErr error) (err error) { 57 if inErr != nil { 58 return inErr 59 } 60 if info.IsDir() { 61 return 62 } 63 relPath, err := filepath.Rel(localRoot, path) 64 if err != nil { 65 return fmt.Errorf("[qiniuUploadDir] filepath.Rel err:%s", err.Error()) 66 } 67 remotePath := NormalizeRemotePath(filepath.Join(remoteRoot, relPath)) 68 expectHash, err := ComputeHashFromFile(path) 69 if err != nil { 70 return 71 } 72 requestList = append(requestList, uploadFileRequest{ 73 localPath: path, 74 remotePath: remotePath, 75 expectHash: expectHash, 76 }) 77 remotePathList = append(remotePathList, remotePath) 78 return 79 }) 80 if len(requestList) == 0 { 81 return ErrNoFile 82 } 83 batchRet, err := ctx.BatchStat(remotePathList) 84 if err != nil { 85 return 86 } 87 for i, ret := range batchRet { 88 i := i 89 if ret.IsExist() && ret.Hash == requestList[i].expectHash { 90 continue 91 } 92 tm.AddTask(func() (err error) { 93 return UploadFileWithHash(ctx, requestList[i].localPath, requestList[i].remotePath, requestList[i].expectHash) 94 }) 95 } 96 ////群发状态询问消息减少网络连接数量,加快速度 97 //entryPathList := make([]rs.EntryPath, len(requestList)) 98 //for i, req := range requestList { 99 // entryPathList[i].Bucket = ctx.bucket 100 // entryPathList[i].Key = req.remotePath 101 //} 102 //batchRet, err := ctx.client.BatchStat(nil, entryPathList) 103 ///* 104 // //此处返回的错误很奇怪,有大量文件不存在信息,应该是正常情况,此处最简单的解决方案就是假设没有错误 105 // if err != nil{ 106 // fmt.Printf("%T %#v\n",err,err) 107 // err1,ok:=err.(*rpc.ErrorInfo) 108 // if !ok{ 109 // return err 110 // } 111 // if err1.Code!=298{ 112 // return err 113 // } 114 // } 115 //*/ 116 //if len(batchRet) != len(entryPathList) { 117 // return fmt.Errorf("[UploadDirMulitThread] len(batchRet)[%d]!=len(entryPathList)[%d] err[%s]", 118 // len(batchRet), len(entryPathList),err) 119 //} 120 //for i, ret := range batchRet { 121 // i := i 122 // //验证hash,当文件不存在时,err是空 123 // if ret.Error != "" && ret.Error != "no such file or directory" { 124 // return fmt.Errorf("[UploadDirMulitThread] [remotePath:%s]ctx.client.BatchStat err[%s]", 125 // requestList[i].remotePath, ret.Error) 126 // } 127 // if ret.Data.Hash == requestList[i].expectHash { 128 // continue 129 // } 130 // tm.AddTask(func() (err error) { 131 // return UploadFileWithHash(ctx, requestList[i].localPath, requestList[i].remotePath, requestList[i].expectHash) 132 // }) 133 //} 134 tm.Wait() 135 if err != nil { 136 return err 137 } 138 err = tm.GetError() 139 return 140 } 141 142 //上传文件,检查同名和同内容文件 143 //先找cdn上是不是已经有一样的文件了,以便分文件断点续传,再上传 144 func UploadFileCheckExist(ctx *Context, localPath string, remotePath string) (err error) { 145 remotePath = NormalizeRemotePath(remotePath) 146 entry, err := ctx.client.Stat(nil, ctx.bucket, remotePath) 147 if err != nil { 148 if !(err.(*rpc.ErrorInfo) != nil && err.(*rpc.ErrorInfo).Err == "no such file or directory") { 149 return err 150 } 151 } 152 expectHash, err := ComputeHashFromFile(localPath) 153 if err != nil { 154 return 155 } 156 //already have a file with same context and same key,do nothing 157 if entry.Hash == expectHash { 158 return 159 } 160 return UploadFileWithHash(ctx, localPath, remotePath, expectHash) 161 } 162 163 //上传文件,检查返回的hash和需要的hash是否一致 164 func UploadFileWithHash(ctx *Context, localPath string, remotePath string, expectHash string) (err error) { 165 var ret qiniuIo.PutRet 166 var extra = &qiniuIo.PutExtra{ 167 CheckCrc: 1, 168 } 169 putPolicy := rs.PutPolicy{ 170 Scope: ctx.bucket + ":" + remotePath, 171 } 172 uptoken := putPolicy.Token(nil) 173 err = qiniuIo.PutFile(nil, &ret, uptoken, remotePath, localPath, extra) 174 //fmt.Println(localPath,remotePath,err) 175 if err != nil { 176 return 177 } 178 if ret.Hash != expectHash { 179 return fmt.Errorf("[UploadFileWithHash][remotePath:%s] ret.Hash:[%s]!=expectHash[%s] ", remotePath, ret.Hash, expectHash) 180 } 181 182 return 183 }