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  }