github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/uploader/upload_manager.go (about) 1 package uploader 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "net/http" 8 "os" 9 "path/filepath" 10 "strings" 11 12 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 13 httpclient "github.com/qiniu/go-sdk/v7/storagev2/http_client" 14 resumablerecorder "github.com/qiniu/go-sdk/v7/storagev2/uploader/resumable_recorder" 15 "github.com/qiniu/go-sdk/v7/storagev2/uptoken" 16 "golang.org/x/sync/errgroup" 17 ) 18 19 type ( 20 // 上传器 21 UploadManager struct { 22 options httpclient.Options 23 upTokenProvider uptoken.Provider 24 resumableRecorder resumablerecorder.ResumableRecorder 25 partSize uint64 26 multiPartsThreshold uint64 27 concurrency int 28 multiPartsUploaderVersion MultiPartsUploaderVersion 29 } 30 31 // 上传器选项 32 UploadManagerOptions struct { 33 // HTTP 客户端选项 34 httpclient.Options 35 36 // 上传凭证接口 37 UpTokenProvider uptoken.Provider 38 39 // 可恢复记录,如果不设置,则无法进行断点续传 40 ResumableRecorder resumablerecorder.ResumableRecorder 41 42 // 分片大小,如果不填写,默认为 4 MB 43 PartSize uint64 44 45 // 分片上传阈值,如果不填写,默认为 4 MB 46 MultiPartsThreshold uint64 47 48 // 分片上传并行度,如果不填写,默认为 1 49 Concurrency int 50 51 // 分片上传版本,如果不填写,默认为 V2 52 MultiPartsUploaderVersion MultiPartsUploaderVersion 53 } 54 55 // 分片上传版本 56 MultiPartsUploaderVersion uint8 57 ) 58 59 const ( 60 // 分片上传 V1 61 MultiPartsUploaderVersionV1 MultiPartsUploaderVersion = 1 62 63 // 分片上传 V2 64 MultiPartsUploaderVersionV2 MultiPartsUploaderVersion = 2 65 ) 66 67 // 创建上传器 68 func NewUploadManager(options *UploadManagerOptions) *UploadManager { 69 if options == nil { 70 options = &UploadManagerOptions{} 71 } 72 partSize := options.PartSize 73 if partSize == 0 { 74 partSize = 1 << 22 75 } else if partSize < (1 << 20) { 76 partSize = 1 << 20 77 } else if partSize > (1 << 30) { 78 partSize = 1 << 30 79 } 80 multiPartsThreshold := options.MultiPartsThreshold 81 if multiPartsThreshold == 0 { 82 multiPartsThreshold = partSize 83 } 84 concurrency := options.Concurrency 85 if concurrency == 0 { 86 concurrency = 4 87 } 88 uploadManager := UploadManager{ 89 options: options.Options, 90 upTokenProvider: options.UpTokenProvider, 91 resumableRecorder: options.ResumableRecorder, 92 partSize: partSize, 93 multiPartsThreshold: multiPartsThreshold, 94 concurrency: concurrency, 95 multiPartsUploaderVersion: options.MultiPartsUploaderVersion, 96 } 97 return &uploadManager 98 } 99 100 // 上传目录 101 func (uploadManager *UploadManager) UploadDirectory(ctx context.Context, directoryPath string, directoryOptions *DirectoryOptions) error { 102 if directoryOptions == nil { 103 directoryOptions = &DirectoryOptions{} 104 } else { 105 tmp := *directoryOptions 106 directoryOptions = &tmp 107 } 108 109 objectConcurrency := directoryOptions.ObjectConcurrency 110 if objectConcurrency == 0 { 111 objectConcurrency = 4 112 } 113 pathSeparator := directoryOptions.PathSeparator 114 if pathSeparator == "" { 115 pathSeparator = "/" 116 } 117 118 if !strings.HasSuffix(directoryPath, string(filepath.Separator)) { 119 directoryPath += string(filepath.Separator) 120 } 121 122 updateObjectName := directoryOptions.UpdateObjectName 123 if updateObjectName == nil { 124 updateObjectName = func(path string) string { return path } 125 } 126 generateObjectName := func(path string) string { 127 path = strings.TrimPrefix(path, directoryPath) 128 if pathSeparator != string(filepath.Separator) { 129 path = strings.Replace(path, string(filepath.Separator), pathSeparator, -1) 130 } 131 return updateObjectName(path) 132 } 133 134 g, ctx := errgroup.WithContext(ctx) 135 g.SetLimit(objectConcurrency) 136 137 err := filepath.Walk(directoryPath, func(path string, info os.FileInfo, err error) error { 138 if err != nil { 139 return err 140 } 141 g.Go(func() error { 142 objectName := generateObjectName(path) 143 if info.Mode().IsRegular() { 144 objectOptions := ObjectOptions{ 145 RegionsProvider: directoryOptions.RegionsProvider, 146 UpToken: directoryOptions.UpToken, 147 BucketName: directoryOptions.BucketName, 148 ObjectName: &objectName, 149 FileName: filepath.Base(path), 150 } 151 if directoryOptions.ShouldUploadObject != nil && !directoryOptions.ShouldUploadObject(path, &objectOptions) { 152 return nil 153 } 154 if directoryOptions.BeforeObjectUpload != nil { 155 directoryOptions.BeforeObjectUpload(path, &objectOptions) 156 } 157 if directoryOptions.OnUploadingProgress != nil { 158 objectOptions.OnUploadingProgress = func(progress *UploadingProgress) { 159 directoryOptions.OnUploadingProgress(path, progress) 160 } 161 } 162 err = uploadManager.UploadFile(ctx, path, &objectOptions, nil) 163 if err == nil && directoryOptions.OnObjectUploaded != nil { 164 directoryOptions.OnObjectUploaded(path, &UploadedObjectInfo{Size: uint64(info.Size())}) 165 } 166 } else if directoryOptions.ShouldCreateDirectory && info.IsDir() { 167 if directoryOptions.ShouldUploadObject != nil && !directoryOptions.ShouldUploadObject(path, nil) { 168 return nil 169 } 170 if !strings.HasSuffix(objectName, pathSeparator) { 171 objectName += pathSeparator 172 } 173 objectOptions := ObjectOptions{ 174 RegionsProvider: directoryOptions.RegionsProvider, 175 UpToken: directoryOptions.UpToken, 176 BucketName: directoryOptions.BucketName, 177 ObjectName: &objectName, 178 FileName: filepath.Base(path), 179 } 180 err = uploadManager.UploadReader(ctx, http.NoBody, &objectOptions, nil) 181 } 182 return err 183 }) 184 return nil 185 }) 186 if err != nil { 187 return err 188 } 189 190 return g.Wait() 191 } 192 193 // 上传文件 194 func (uploadManager *UploadManager) UploadFile(ctx context.Context, path string, objectOptions *ObjectOptions, returnValue interface{}) error { 195 if objectOptions == nil { 196 objectOptions = &ObjectOptions{} 197 } else { 198 tmp := *objectOptions 199 objectOptions = &tmp 200 } 201 202 fileInfo, err := os.Stat(path) 203 if err != nil { 204 return err 205 } 206 207 var uploader Uploader 208 if fileInfo.Size() > int64(uploadManager.multiPartsThreshold) { 209 uploader = newMultiPartsUploader(uploadManager.getScheduler()) 210 } else { 211 uploader = uploadManager.getFormUploader() 212 } 213 214 return uploader.UploadFile(ctx, path, objectOptions, returnValue) 215 } 216 217 // 上传 io.Reader 218 func (uploadManager *UploadManager) UploadReader(ctx context.Context, reader io.Reader, objectOptions *ObjectOptions, returnValue interface{}) error { 219 var uploader Uploader 220 221 if objectOptions == nil { 222 objectOptions = &ObjectOptions{} 223 } else { 224 tmp := *objectOptions 225 objectOptions = &tmp 226 } 227 228 if rscs, ok := reader.(io.ReadSeeker); ok && canSeekReally(rscs) { 229 size, err := getSeekerSize(rscs) 230 if err == nil && size > uploadManager.multiPartsThreshold { 231 uploader = newMultiPartsUploader(uploadManager.getScheduler()) 232 } 233 } 234 if uploader == nil { 235 firstPartBytes, err := internal_io.ReadAll(io.LimitReader(reader, int64(uploadManager.multiPartsThreshold+1))) 236 if err != nil { 237 return err 238 } 239 reader = io.MultiReader(bytes.NewReader(firstPartBytes), reader) 240 if len(firstPartBytes) > int(uploadManager.multiPartsThreshold) { 241 uploader = newMultiPartsUploader(uploadManager.getScheduler()) 242 } else { 243 uploader = uploadManager.getFormUploader() 244 } 245 } 246 247 return uploader.UploadReader(ctx, reader, objectOptions, returnValue) 248 } 249 250 func (uploadManager *UploadManager) getScheduler() multiPartsUploaderScheduler { 251 if uploadManager.concurrency > 1 { 252 return newConcurrentMultiPartsUploaderScheduler(uploadManager.getMultiPartsUploader(), &concurrentMultiPartsUploaderSchedulerOptions{ 253 PartSize: uploadManager.partSize, Concurrency: uploadManager.concurrency, 254 }) 255 } else { 256 return newSerialMultiPartsUploaderScheduler(uploadManager.getMultiPartsUploader(), &serialMultiPartsUploaderSchedulerOptions{ 257 PartSize: uploadManager.partSize, 258 }) 259 } 260 } 261 262 func (uploadManager *UploadManager) getMultiPartsUploader() MultiPartsUploader { 263 multiPartsUploaderOptions := MultiPartsUploaderOptions{ 264 Options: uploadManager.options, 265 UpTokenProvider: uploadManager.upTokenProvider, 266 ResumableRecorder: uploadManager.resumableRecorder, 267 } 268 if uploadManager.multiPartsUploaderVersion == MultiPartsUploaderVersionV1 { 269 return NewMultiPartsUploaderV1(&multiPartsUploaderOptions) 270 } else { 271 return NewMultiPartsUploaderV2(&multiPartsUploaderOptions) 272 } 273 } 274 275 func (uploadManager *UploadManager) getFormUploader() Uploader { 276 return NewFormUploader(&FormUploaderOptions{ 277 Options: uploadManager.options, 278 UpToken: uploadManager.upTokenProvider, 279 }) 280 }