github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/uploader/multi_parts_uploader_v1.go (about) 1 package uploader 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "strings" 8 "time" 9 10 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 11 "github.com/qiniu/go-sdk/v7/storagev2/apis" 12 "github.com/qiniu/go-sdk/v7/storagev2/retrier" 13 resumablerecorder "github.com/qiniu/go-sdk/v7/storagev2/uploader/resumable_recorder" 14 "github.com/qiniu/go-sdk/v7/storagev2/uploader/source" 15 ) 16 17 type ( 18 multiPartsUploaderV1 struct { 19 storage *apis.Storage 20 options *MultiPartsUploaderOptions 21 } 22 23 multiPartsUploaderV1InitializedParts struct { 24 multiPartsObjectOptions *MultiPartsObjectOptions 25 records map[uint64]resumedMultiPartsUploaderV1Record 26 medium resumablerecorder.WriteableResumableRecorderMedium 27 src source.Source 28 } 29 30 multiPartsUploaderV1UploadedPart struct { 31 ctx string 32 crc32 uint32 33 partNumber, offset, size uint64 34 } 35 36 resumedMultiPartsUploaderV1Record struct { 37 ctx string 38 crc32 uint32 39 offset, size uint64 40 expiredAt time.Time 41 } 42 ) 43 44 // 创建分片上传器 V1 45 func NewMultiPartsUploaderV1(options *MultiPartsUploaderOptions) MultiPartsUploader { 46 if options == nil { 47 options = &MultiPartsUploaderOptions{} 48 } 49 return &multiPartsUploaderV1{apis.NewStorage(&options.Options), options} 50 } 51 52 func (uploader *multiPartsUploaderV1) InitializeParts(ctx context.Context, src source.Source, multiPartsObjectOptions *MultiPartsObjectOptions) (InitializedParts, error) { 53 if multiPartsObjectOptions == nil { 54 multiPartsObjectOptions = &MultiPartsObjectOptions{} 55 } 56 if multiPartsObjectOptions.PartSize == 0 { 57 multiPartsObjectOptions.PartSize = 1 << 22 58 } 59 medium := tryToOpenResumableRecorderForAppending(ctx, src, multiPartsObjectOptions, uploader.options) 60 return &multiPartsUploaderV1InitializedParts{multiPartsObjectOptions, nil, medium, src}, nil 61 } 62 63 func (uploader *multiPartsUploaderV1) TryToResume(ctx context.Context, src source.Source, multiPartsObjectOptions *MultiPartsObjectOptions) InitializedParts { 64 if multiPartsObjectOptions == nil { 65 multiPartsObjectOptions = &MultiPartsObjectOptions{} 66 } 67 if multiPartsObjectOptions.PartSize == 0 { 68 multiPartsObjectOptions.PartSize = 1 << 22 69 } 70 71 readableMedium := tryToOpenResumableRecorderForReading(ctx, src, multiPartsObjectOptions, uploader.options) 72 if readableMedium == nil { 73 return nil 74 } 75 defer readableMedium.Close() 76 77 records := make(map[uint64]resumedMultiPartsUploaderV1Record) 78 for { 79 var record resumablerecorder.ResumableRecord 80 if err := readableMedium.Next(&record); err != nil { 81 break 82 } 83 records[record.PartNumber] = resumedMultiPartsUploaderV1Record{ 84 ctx: record.PartID, 85 crc32: record.CRC32, 86 offset: record.Offset, 87 size: record.PartSize, 88 expiredAt: record.ExpiredAt, 89 } 90 } 91 readableMedium.Close() 92 93 medium := tryToOpenResumableRecorderForAppending(ctx, src, multiPartsObjectOptions, uploader.options) 94 return &multiPartsUploaderV1InitializedParts{multiPartsObjectOptions, records, medium, src} 95 } 96 97 func (uploader *multiPartsUploaderV1) UploadPart(ctx context.Context, initialized InitializedParts, part source.Part, options *UploadPartOptions) (UploadedPart, error) { 98 initializedParts, ok := initialized.(*multiPartsUploaderV1InitializedParts) 99 if !ok { 100 return nil, errors.New("unrecognized initialized parts") 101 } 102 if len(initializedParts.records) > 0 { 103 if record, ok := initializedParts.records[part.PartNumber()]; ok { 104 if record.offset == part.Offset() && record.size == part.Size() { 105 if options != nil && options.OnUploadingProgress != nil { 106 options.OnUploadingProgress(&UploadingPartProgress{Uploaded: record.size, PartSize: record.size}) 107 } 108 return multiPartsUploaderV1UploadedPart{ 109 ctx: record.ctx, 110 crc32: record.crc32, 111 offset: record.offset, 112 size: record.size, 113 partNumber: part.PartNumber(), 114 }, nil 115 } 116 } 117 } 118 return uploader.uploadPart(ctx, initializedParts, part, options) 119 } 120 121 func (uploader *multiPartsUploaderV1) uploadPart(ctx context.Context, initialized *multiPartsUploaderV1InitializedParts, part source.Part, options *UploadPartOptions) (UploadedPart, error) { 122 apisOptions := apis.Options{ 123 OverwrittenRegion: initialized.multiPartsObjectOptions.RegionsProvider, 124 } 125 if options != nil && options.OnUploadingProgress != nil { 126 apisOptions.OnRequestProgress = func(uploaded, totalSize uint64) { 127 options.OnUploadingProgress(&UploadingPartProgress{Uploaded: uploaded, PartSize: totalSize}) 128 } 129 } 130 upToken, err := getUpToken(uploader.options.Credentials, &initialized.multiPartsObjectOptions.ObjectOptions, uploader.options.UpTokenProvider) 131 if err != nil { 132 return nil, err 133 } 134 135 response, err := uploader.storage.ResumableUploadV1MakeBlock(ctx, &apis.ResumableUploadV1MakeBlockRequest{ 136 BlockSize: int64(part.Size()), 137 UpToken: upToken, 138 Body: internal_io.MakeReadSeekCloserFromReader(part), 139 }, &apisOptions) 140 if err != nil { 141 return nil, err 142 } else if response.Crc32 > 0 { 143 if _, err = part.Seek(0, io.SeekStart); err != nil { 144 return nil, err 145 } 146 crc32, err := crc32FromReadSeeker(part) 147 if err != nil { 148 return nil, err 149 } 150 if crc32 != uint32(response.Crc32) { 151 return nil, errors.New("unexpected crc32") 152 } 153 } 154 155 if medium := initialized.medium; medium != nil { 156 medium.Write(&resumablerecorder.ResumableRecord{ 157 PartID: response.Ctx, 158 Offset: part.Offset(), 159 PartSize: part.Size(), 160 PartNumber: part.PartNumber(), 161 ExpiredAt: time.Unix(response.ExpiredAt, 0), 162 CRC32: uint32(response.Crc32), 163 }) 164 } 165 166 return multiPartsUploaderV1UploadedPart{ 167 ctx: response.Ctx, 168 crc32: uint32(response.Crc32), 169 offset: part.Offset(), 170 size: part.Size(), 171 partNumber: part.PartNumber(), 172 }, nil 173 } 174 175 func (uploader *multiPartsUploaderV1) CompleteParts(ctx context.Context, initialized InitializedParts, parts []UploadedPart, returnValue interface{}) error { 176 initializedParts, ok := initialized.(*multiPartsUploaderV1InitializedParts) 177 if !ok { 178 return errors.New("unrecognized initialized parts") 179 } 180 options := apis.Options{ 181 OverwrittenRegion: initializedParts.multiPartsObjectOptions.RegionsProvider, 182 } 183 upToken, err := getUpToken(uploader.options.Credentials, &initializedParts.multiPartsObjectOptions.ObjectOptions, uploader.options.UpTokenProvider) 184 if err != nil { 185 return err 186 } 187 188 var ( 189 ctxs = make([]string, 0, len(parts)) 190 size uint64 191 ) 192 for _, part := range parts { 193 uploadedPart, ok := part.(multiPartsUploaderV1UploadedPart) 194 if !ok { 195 return errors.New("unrecognized uploaded part") 196 } 197 ctxs = append(ctxs, uploadedPart.ctx) 198 size += uploadedPart.size 199 } 200 201 _, err = uploader.storage.ResumableUploadV1MakeFile(ctx, &apis.ResumableUploadV1MakeFileRequest{ 202 Size: int64(size), 203 ObjectName: initializedParts.multiPartsObjectOptions.ObjectName, 204 FileName: initializedParts.multiPartsObjectOptions.FileName, 205 MimeType: initializedParts.multiPartsObjectOptions.ContentType, 206 CustomData: mergeCustomVarsAndMetadata(initializedParts.multiPartsObjectOptions.Metadata, initializedParts.multiPartsObjectOptions.CustomVars), 207 UpToken: upToken, 208 Body: internal_io.NewBytesNopCloser([]byte(strings.Join(ctxs, ","))), 209 ResponseBody: returnValue, 210 }, &options) 211 if err == nil || !retrier.IsErrorRetryable(err) { 212 if medium := initializedParts.medium; medium != nil { 213 medium.Close() 214 } 215 initializedParts.medium = nil 216 tryToDeleteResumableRecorderMedium(ctx, initializedParts.src, initializedParts.multiPartsObjectOptions, uploader.options) 217 } 218 return err 219 } 220 221 func (uploader *multiPartsUploaderV1) MultiPartsUploaderOptions() *MultiPartsUploaderOptions { 222 return uploader.options 223 } 224 225 func (initialized *multiPartsUploaderV1InitializedParts) Close() error { 226 if initialized.medium != nil { 227 return initialized.medium.Close() 228 } 229 return nil 230 } 231 232 func (uploadedPart multiPartsUploaderV1UploadedPart) Offset() uint64 { 233 return uploadedPart.offset 234 } 235 236 func (uploadedPart multiPartsUploaderV1UploadedPart) PartNumber() uint64 { 237 return uploadedPart.partNumber 238 } 239 240 func (uploadedPart multiPartsUploaderV1UploadedPart) PartSize() uint64 { 241 return uploadedPart.size 242 } 243 244 func (uploadedPart multiPartsUploaderV1UploadedPart) Crc32() uint32 { 245 return uploadedPart.crc32 246 } 247 248 func (uploadedPart multiPartsUploaderV1UploadedPart) Ctx() string { 249 return uploadedPart.ctx 250 }