github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/baidupcs/upload.go (about) 1 package baidupcs 2 3 import ( 4 "errors" 5 "math/rand" 6 "net/http" 7 "path" 8 "strings" 9 "time" 10 11 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror" 12 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" 13 ) 14 15 const ( 16 // MaxUploadBlockSize 最大上传的文件分片大小 17 MaxUploadBlockSize = 2 * converter.GB 18 // MinUploadBlockSize 最小的上传的文件分片大小 19 MinUploadBlockSize = 4 * converter.MB 20 // MaxRapidUploadSize 秒传文件支持的最大文件大小 21 MaxRapidUploadSize = 20 * converter.GB 22 // RecommendUploadBlockSize 推荐的上传的文件分片大小 23 RecommendUploadBlockSize = 1 * converter.GB 24 // SliceMD5Size 计算 slice-md5 所需的长度 25 SliceMD5Size = 256 * converter.KB 26 // EmptyContentMD5 空串的md5 27 EmptyContentMD5 = "d41d8cd98f00b204e9800998ecf8427e" 28 ) 29 30 var ( 31 // ErrUploadMD5NotFound 未找到md5 32 ErrUploadMD5NotFound = errors.New("unknown response data, md5 not found") 33 // ErrUploadSavePathFound 未找到保存路径 34 ErrUploadSavePathFound = errors.New("unknown response data, file saved path not found") 35 // ErrUploadSeqNotMatch 服务器返回的上传队列不匹配 36 ErrUploadSeqNotMatch = errors.New("服务器返回的上传队列不匹配") 37 // ErrUploadMD5Unknown 服务器无匹配文件/秒传未生效 38 ErrUploadMD5Unknown = errors.New("服务器无匹配文件/秒传未生效") 39 // ErrUploadFileExists 文件或目录已存在 40 ErrUploadFileExists = errors.New("文件已存在") 41 ) 42 43 type ( 44 // UploadFunc 上传文件处理函数 45 UploadFunc func(uploadURL string, jar http.CookieJar) (resp *http.Response, err error) 46 47 // RapidUploadInfo 文件秒传信息 48 RapidUploadInfo struct { 49 Filename string 50 ContentLength int64 51 ContentMD5 string 52 SliceMD5 string 53 ContentCrc32 string 54 } 55 56 uploadJSON struct { 57 *PathJSON 58 *pcserror.PCSErrInfo 59 } 60 61 uploadTmpFileJSON struct { 62 MD5 string `json:"md5"` 63 *pcserror.PCSErrInfo 64 } 65 66 uploadPrecreateJSON struct { 67 ReturnType int `json:"return_type"` // 1上传, 2秒传 68 UploadID string `json:"uploadid"` 69 BlockList []int `json:"block_list"` 70 *pcserror.PanErrorInfo 71 fdJSON `json:"info"` 72 } 73 74 uploadCreateJSON struct { 75 ErrNo int `json:"errno"` // 0成功, 2失败 76 Path string `json:"path"` 77 *pcserror.PanErrorInfo 78 } 79 80 // UploadSeq 分片上传顺序 81 UploadSeq struct { 82 Seq int 83 Block string 84 } 85 86 // PrecreateInfo 预提交文件消息返回数据 87 PrecreateInfo struct { 88 IsRapidUpload bool 89 UploadID string 90 UploadSeqList []*UploadSeq 91 } 92 93 uploadSuperfile2JSON struct { 94 MD5 string `json:"md5"` 95 *pcserror.PCSErrInfo 96 } 97 ) 98 99 func randomifyMD5(md5 string) string { 100 r := rand.New(rand.NewSource(time.Now().UnixNano())) 101 newmd5bytes := []byte(strings.ToLower(md5)) 102 uppermd5 := []byte(strings.ToUpper(md5)) 103 for i := range md5 { 104 if r.Float32() > 0.6 { 105 newmd5bytes[i] = uppermd5[i] 106 } 107 } 108 return string(newmd5bytes) 109 } 110 111 // RapidUpload 秒传文件 112 func (pcs *BaiduPCS) RapidUpload(targetPath, contentMD5, sliceMD5, dataContent, crc32 string, offset, length, totalSize, dataTime int64) (pcsError pcserror.Error) { 113 defer func() { 114 if pcsError == nil { 115 // 更新缓存 116 pcs.deleteCache([]string{path.Dir(targetPath)}) 117 } 118 }() 119 pcsError = pcs.rapidUploadV2(targetPath, strings.ToLower(contentMD5), strings.ToLower(sliceMD5), dataContent, crc32, offset, length, totalSize, dataTime) 120 return 121 } 122 123 // APIRapidUpload openapi秒传文件 124 func (pcs *BaiduPCS) APIRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) { 125 defer func() { 126 if pcsError == nil { 127 // 更新缓存 128 pcs.deleteCache([]string{path.Dir(targetPath)}) 129 } 130 }() 131 pcsError = pcs.rapidUpload(targetPath, strings.ToLower(contentMD5), strings.ToLower(sliceMD5), "", length) 132 return 133 } 134 135 func (pcs *BaiduPCS) rapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) { 136 dataReadCloser, pcsError := pcs.PrepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length) 137 if pcsError != nil { 138 return 139 } 140 defer dataReadCloser.Close() 141 return pcserror.DecodePanJSONError(OperationRapidUpload, dataReadCloser) 142 } 143 144 func (pcs *BaiduPCS) rapidUploadV2(targetPath, contentMD5, sliceMD5, dataContent, crc32 string, offset, length, totalSize, dataTime int64) (pcsError pcserror.Error) { 145 dataReadCloser, pcsError := pcs.PrepareRapidUploadV2(targetPath, contentMD5, sliceMD5, dataContent, crc32, offset, length, totalSize, dataTime) 146 if pcsError != nil { 147 return 148 } 149 defer dataReadCloser.Close() 150 return pcserror.DecodeXPanJSONError(OperationRapidUpload, dataReadCloser) 151 } 152 153 // RapidUploadNoCheckDir 秒传文件, 不进行目录检查, 会覆盖掉同名的目录! 154 func (pcs *BaiduPCS) RapidUploadNoCheckDir(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) { 155 dataReadCloser, pcsError := pcs.prepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length) 156 if pcsError != nil { 157 return 158 } 159 160 defer dataReadCloser.Close() 161 162 pcsError = pcserror.DecodePCSJSONError(OperationRapidUpload, dataReadCloser) 163 if pcsError != nil { 164 return 165 } 166 167 return nil 168 } 169 170 // Upload 上传单个文件 171 func (pcs *BaiduPCS) Upload(policy, targetPath string, uploadFunc UploadFunc) (pcsError pcserror.Error, newpath string) { 172 dataReadCloser, pcsError := pcs.PrepareUpload(policy, targetPath, uploadFunc) 173 if pcsError != nil { 174 return pcsError, "" 175 } 176 177 defer dataReadCloser.Close() 178 179 // 数据处理 180 jsonData := uploadJSON{ 181 PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUpload), 182 } 183 184 pcsError = pcserror.HandleJSONParse(OperationUpload, dataReadCloser, &jsonData) 185 if pcsError != nil { 186 return 187 } 188 189 if jsonData.Path == "" { 190 jsonData.PCSErrInfo.ErrType = pcserror.ErrTypeInternalError 191 jsonData.PCSErrInfo.Err = ErrUploadSavePathFound 192 return jsonData.PCSErrInfo, "" 193 } 194 195 // 更新缓存 196 pcs.deleteCache([]string{path.Dir(targetPath)}) 197 return nil, jsonData.Path 198 } 199 200 // UploadTmpFile 分片上传—文件分片及上传 201 func (pcs *BaiduPCS) UploadTmpFile(uploadFunc UploadFunc) (md5 string, pcsError pcserror.Error) { 202 dataReadCloser, pcsError := pcs.PrepareUploadTmpFile(uploadFunc) 203 if pcsError != nil { 204 return "", pcsError 205 } 206 207 defer dataReadCloser.Close() 208 209 // 数据处理 210 jsonData := uploadTmpFileJSON{ 211 PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUploadTmpFile), 212 } 213 214 pcsError = pcserror.HandleJSONParse(OperationUploadTmpFile, dataReadCloser, &jsonData) 215 if pcsError != nil { 216 return 217 } 218 219 // 未找到md5 220 if jsonData.MD5 == "" { 221 jsonData.PCSErrInfo.ErrType = pcserror.ErrTypeInternalError 222 jsonData.PCSErrInfo.Err = ErrUploadMD5NotFound 223 return "", jsonData.PCSErrInfo 224 } 225 226 return jsonData.MD5, nil 227 } 228 229 // UploadCreateSuperFile 分片上传—合并分片文件 230 func (pcs *BaiduPCS) UploadCreateSuperFile(policy string, checkDir bool, targetPath string, blockList ...string) (pcsError pcserror.Error) { 231 dataReadCloser, pcsError := pcs.PrepareUploadCreateSuperFile(policy, checkDir, targetPath, blockList...) 232 if pcsError != nil { 233 return pcsError 234 } 235 236 defer dataReadCloser.Close() 237 238 errInfo := pcserror.DecodePCSJSONError(OperationUploadCreateSuperFile, dataReadCloser) 239 if errInfo != nil { 240 return errInfo 241 } 242 243 // 更新缓存, targetPath取了dir所以不受重命名策略影响 244 pcs.deleteCache([]string{path.Dir(targetPath)}) 245 return nil 246 } 247 248 // UploadPrecreate 分片上传—Precreate, 249 // 支持检验秒传 250 func (pcs *BaiduPCS) UploadPrecreate(targetPath, contentMD5, sliceMD5, crc32 string, size int64, bolckList ...string) (precreateInfo *PrecreateInfo, pcsError pcserror.Error) { 251 dataReadCloser, pcsError := pcs.PrepareUploadPrecreate(targetPath, contentMD5, sliceMD5, crc32, size, bolckList...) 252 if pcsError != nil { 253 return 254 } 255 256 defer dataReadCloser.Close() 257 258 errInfo := pcserror.NewPanErrorInfo(OperationUploadPrecreate) 259 jsonData := uploadPrecreateJSON{ 260 PanErrorInfo: errInfo, 261 } 262 263 pcsError = pcserror.HandleJSONParse(OperationUploadPrecreate, dataReadCloser, &jsonData) 264 if pcsError != nil { 265 return 266 } 267 268 switch jsonData.ReturnType { 269 case 1: // 上传 270 seqLen := len(jsonData.BlockList) 271 if seqLen != len(bolckList) { 272 errInfo.ErrType = pcserror.ErrTypeRemoteError 273 errInfo.Err = ErrUploadSeqNotMatch 274 return nil, errInfo 275 } 276 277 seqList := make([]*UploadSeq, 0, seqLen) 278 for k, seq := range jsonData.BlockList { 279 seqList = append(seqList, &UploadSeq{ 280 Seq: seq, 281 Block: bolckList[k], 282 }) 283 } 284 return &PrecreateInfo{ 285 UploadID: jsonData.UploadID, 286 UploadSeqList: seqList, 287 }, nil 288 289 case 2: // 秒传 290 return &PrecreateInfo{ 291 IsRapidUpload: true, 292 }, nil 293 294 default: 295 panic("unknown returntype") 296 } 297 } 298 299 // UploadSuperfile2 分片上传—Superfile2 300 func (pcs *BaiduPCS) UploadSuperfile2(uploadid, targetPath string, partseq int, partOffset int64, uploadFunc UploadFunc) (md5sum string, pcsError pcserror.Error) { 301 dataReadCloser, pcsError := pcs.PrepareUploadSuperfile2(uploadid, targetPath, partseq, partOffset, uploadFunc) 302 if pcsError != nil { 303 return 304 } 305 306 defer dataReadCloser.Close() 307 308 jsonData := uploadSuperfile2JSON{ 309 PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUploadSuperfile2), 310 } 311 312 pcsError = pcserror.HandleJSONParse(OperationUploadSuperfile2, dataReadCloser, &jsonData) 313 if pcsError != nil { 314 return 315 } 316 317 return jsonData.MD5, nil 318 }