github.com/qiniu/go-sdk/v7@v7.27.0/storage/resume_uploader_apis.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"strings"
     7  	"time"
     8  
     9  	internal_io "github.com/qiniu/go-sdk/v7/internal/io"
    10  	"github.com/qiniu/go-sdk/v7/storagev2/apis"
    11  	"github.com/qiniu/go-sdk/v7/storagev2/apis/resumable_upload_v2_complete_multipart_upload"
    12  	"github.com/qiniu/go-sdk/v7/storagev2/region"
    13  	"github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    14  )
    15  
    16  type resumeUploaderAPIs struct {
    17  	cfg     *Config
    18  	storage *apis.Storage
    19  }
    20  
    21  // BlkputRet 表示分片上传每个片上传完毕的返回值
    22  type BlkputRet struct {
    23  	Ctx        string `json:"ctx"`
    24  	Checksum   string `json:"checksum"`
    25  	Crc32      uint32 `json:"crc32"`
    26  	Offset     uint32 `json:"offset"`
    27  	Host       string `json:"host"`
    28  	ExpiredAt  int64  `json:"expired_at"`
    29  	chunkSize  int
    30  	fileOffset int64
    31  	blkIdx     int
    32  }
    33  
    34  func (p *resumeUploaderAPIs) mkBlk(
    35  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider,
    36  	ret *BlkputRet, blockSize int64, body io.Reader, size int64,
    37  ) error {
    38  	response, err := p.storage.ResumableUploadV1MakeBlock(
    39  		ctx,
    40  		&apis.ResumableUploadV1MakeBlockRequest{
    41  			BlockSize: blockSize,
    42  			UpToken:   uptoken.NewParser(upToken),
    43  			Body:      internal_io.MakeReadSeekCloserFromReader(body),
    44  		},
    45  		makeApiOptionsFromUpEndpoints(upEndpoints),
    46  	)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	*ret = BlkputRet{
    51  		Ctx:       response.Ctx,
    52  		Checksum:  response.Checksum,
    53  		Crc32:     uint32(response.Crc32),
    54  		Offset:    uint32(response.Offset),
    55  		Host:      response.Host,
    56  		ExpiredAt: response.ExpiredAt,
    57  	}
    58  	return nil
    59  }
    60  
    61  func (p *resumeUploaderAPIs) bput(
    62  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider,
    63  	ret *BlkputRet, body io.Reader, size int64,
    64  ) error {
    65  	response, err := p.storage.ResumableUploadV1Bput(
    66  		ctx,
    67  		&apis.ResumableUploadV1BputRequest{
    68  			Ctx:         ret.Ctx,
    69  			ChunkOffset: int64(ret.Offset),
    70  			UpToken:     uptoken.NewParser(upToken),
    71  			Body:        internal_io.MakeReadSeekCloserFromReader(body),
    72  		},
    73  		makeApiOptionsFromUpEndpoints(upEndpoints),
    74  	)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	*ret = BlkputRet{
    79  		Ctx:       response.Ctx,
    80  		Checksum:  response.Checksum,
    81  		Crc32:     uint32(response.Crc32),
    82  		Offset:    uint32(response.Offset),
    83  		Host:      response.Host,
    84  		ExpiredAt: response.ExpiredAt,
    85  	}
    86  	return nil
    87  }
    88  
    89  // RputExtra 表示分片上传额外可以指定的参数
    90  type RputExtra struct {
    91  	Recorder Recorder // 可选。上传进度记录
    92  
    93  	// 可选。
    94  	// 用户自定义参数:key 以"x:"开头,而且 value 不能为空 eg: key为x:qqq
    95  	// 自定义 meta:key 以"x-qn-meta-"开头,而且 value 不能为空 eg: key为x-qn-meta-aaa
    96  	Params             map[string]string
    97  	UpHost             string
    98  	MimeType           string                                        // 可选。
    99  	ChunkSize          int                                           // 可选。每次上传的Chunk大小
   100  	TryTimes           int                                           // 可选。尝试次数
   101  	HostFreezeDuration time.Duration                                 // 可选。主备域名冻结时间(默认:600s),当一个域名请求失败(单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
   102  	Progresses         []BlkputRet                                   // 可选。上传进度
   103  	Notify             func(blkIdx int, blkSize int, ret *BlkputRet) // 可选。进度提示(注意多个block是并行传输的)
   104  	NotifyErr          func(blkIdx int, blkSize int, err error)
   105  }
   106  
   107  func (extra *RputExtra) init() {
   108  	if extra.ChunkSize == 0 {
   109  		extra.ChunkSize = settings.ChunkSize
   110  	}
   111  	if extra.TryTimes == 0 {
   112  		extra.TryTimes = settings.TryTimes
   113  	}
   114  	if extra.HostFreezeDuration <= 0 {
   115  		extra.HostFreezeDuration = 10 * 60 * time.Second
   116  	}
   117  	if extra.Notify == nil {
   118  		extra.Notify = func(blkIdx, blkSize int, ret *BlkputRet) {}
   119  	}
   120  	if extra.NotifyErr == nil {
   121  		extra.NotifyErr = func(blkIdx, blkSize int, err error) {}
   122  	}
   123  }
   124  
   125  func (p *resumeUploaderAPIs) mkfile(
   126  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider,
   127  	ret interface{}, key string, hasKey bool, fsize int64, extra *RputExtra,
   128  ) error {
   129  	if extra == nil {
   130  		extra = &RputExtra{}
   131  	}
   132  	ctxs := make([]string, len(extra.Progresses))
   133  	for i, progress := range extra.Progresses {
   134  		ctxs[i] = progress.Ctx
   135  	}
   136  	_, err := p.storage.ResumableUploadV1MakeFile(
   137  		ctx,
   138  		&apis.ResumableUploadV1MakeFileRequest{
   139  			Size:         fsize,
   140  			ObjectName:   makeKeyForUploading(key, hasKey),
   141  			MimeType:     extra.MimeType,
   142  			CustomData:   makeCustomData(extra.Params),
   143  			UpToken:      uptoken.NewParser(upToken),
   144  			Body:         internal_io.MakeReadSeekCloserFromReader(strings.NewReader(strings.Join(ctxs, ","))),
   145  			ResponseBody: ret,
   146  		},
   147  		makeApiOptionsFromUpEndpoints(upEndpoints),
   148  	)
   149  	return err
   150  }
   151  
   152  // InitPartsRet 表示分片上传 v2 初始化完毕的返回值
   153  type InitPartsRet struct {
   154  	UploadID string `json:"uploadId"`
   155  	ExpireAt int64  `json:"expireAt"`
   156  }
   157  
   158  func (p *resumeUploaderAPIs) initParts(
   159  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider,
   160  	bucket, key string, hasKey bool, ret *InitPartsRet,
   161  ) error {
   162  	response, err := p.storage.ResumableUploadV2InitiateMultipartUpload(
   163  		ctx,
   164  		&apis.ResumableUploadV2InitiateMultipartUploadRequest{
   165  			BucketName: bucket,
   166  			ObjectName: makeKeyForUploading(key, hasKey),
   167  			UpToken:    uptoken.NewParser(upToken),
   168  		},
   169  		makeApiOptionsFromUpEndpoints(upEndpoints),
   170  	)
   171  	if err != nil {
   172  		return err
   173  	}
   174  	*ret = InitPartsRet{
   175  		UploadID: response.UploadId,
   176  		ExpireAt: response.ExpiredAt,
   177  	}
   178  	return nil
   179  }
   180  
   181  // UploadPartsRet 表示分片上传 v2 每个片上传完毕的返回值
   182  type UploadPartsRet struct {
   183  	Etag string `json:"etag"`
   184  	MD5  string `json:"md5"`
   185  }
   186  
   187  func (p *resumeUploaderAPIs) uploadParts(
   188  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider,
   189  	bucket, key string, hasKey bool, uploadId string, partNumber int64, partMD5 string, ret *UploadPartsRet, body io.Reader, size int64,
   190  ) error {
   191  	response, err := p.storage.ResumableUploadV2UploadPart(
   192  		ctx,
   193  		&apis.ResumableUploadV2UploadPartRequest{
   194  			BucketName: bucket,
   195  			ObjectName: makeKeyForUploading(key, hasKey),
   196  			UploadId:   uploadId,
   197  			PartNumber: partNumber,
   198  			Md5:        partMD5,
   199  			UpToken:    uptoken.NewParser(upToken),
   200  			Body:       internal_io.MakeReadSeekCloserFromLimitedReader(body, size),
   201  		},
   202  		makeApiOptionsFromUpEndpoints(upEndpoints),
   203  	)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	*ret = UploadPartsRet{
   208  		Etag: response.Etag,
   209  		MD5:  response.Md5,
   210  	}
   211  	return nil
   212  }
   213  
   214  type UploadPartInfo struct {
   215  	Etag       string `json:"etag"`
   216  	PartNumber int64  `json:"partNumber"`
   217  	partSize   int
   218  	fileOffset int64
   219  }
   220  
   221  // RputV2Extra 表示分片上传 v2 额外可以指定的参数
   222  type RputV2Extra struct {
   223  	Recorder           Recorder          // 可选。上传进度记录
   224  	Metadata           map[string]string // 可选。用户自定义文件 metadata 信息
   225  	CustomVars         map[string]string // 可选。用户自定义参数,以"x:"开头,而且值不能为空,否则忽略
   226  	UpHost             string
   227  	MimeType           string                                      // 可选。
   228  	PartSize           int64                                       // 可选。每次上传的块大小
   229  	TryTimes           int                                         // 可选。尝试次数
   230  	HostFreezeDuration time.Duration                               // 可选。主备域名冻结时间(默认:600s),当一个域名请求失败(单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
   231  	Progresses         []UploadPartInfo                            // 上传进度
   232  	Notify             func(partNumber int64, ret *UploadPartsRet) // 可选。进度提示(注意多个block是并行传输的)
   233  	NotifyErr          func(partNumber int64, err error)
   234  }
   235  
   236  func (extra *RputV2Extra) init() {
   237  	if extra.PartSize == 0 {
   238  		extra.PartSize = settings.PartSize
   239  	}
   240  	if extra.TryTimes == 0 {
   241  		extra.TryTimes = settings.TryTimes
   242  	}
   243  	if extra.HostFreezeDuration <= 0 {
   244  		extra.HostFreezeDuration = 10 * 60 * time.Second
   245  	}
   246  	if extra.Notify == nil {
   247  		extra.Notify = func(partNumber int64, ret *UploadPartsRet) {}
   248  	}
   249  	if extra.NotifyErr == nil {
   250  		extra.NotifyErr = func(partNumber int64, err error) {}
   251  	}
   252  }
   253  
   254  func (p *resumeUploaderAPIs) completeParts(
   255  	ctx context.Context, upToken string, upEndpoints region.EndpointsProvider, ret interface{},
   256  	bucket, key string, hasKey bool, uploadId string, extra *RputV2Extra,
   257  ) error {
   258  	parts := make([]resumable_upload_v2_complete_multipart_upload.PartInfo, 0, len(extra.Progresses))
   259  	for i := range extra.Progresses {
   260  		parts = append(parts, resumable_upload_v2_complete_multipart_upload.PartInfo{
   261  			PartNumber: extra.Progresses[i].PartNumber,
   262  			Etag:       extra.Progresses[i].Etag,
   263  		})
   264  	}
   265  	customVars := make(map[string]string, len(extra.CustomVars))
   266  	for k, v := range extra.CustomVars {
   267  		if strings.HasPrefix(k, "x:") && v != "" {
   268  			customVars[k] = v
   269  		}
   270  	}
   271  	_, err := p.storage.ResumableUploadV2CompleteMultipartUpload(
   272  		ctx,
   273  		&apis.ResumableUploadV2CompleteMultipartUploadRequest{
   274  			BucketName:   bucket,
   275  			ObjectName:   makeKeyForUploading(key, hasKey),
   276  			UploadId:     uploadId,
   277  			UpToken:      uptoken.NewParser(upToken),
   278  			Parts:        parts,
   279  			MimeType:     extra.MimeType,
   280  			Metadata:     extra.Metadata,
   281  			CustomVars:   customVars,
   282  			ResponseBody: ret,
   283  		},
   284  		makeApiOptionsFromUpEndpoints(upEndpoints),
   285  	)
   286  	return err
   287  }
   288  
   289  func (p *resumeUploaderAPIs) upHost(ak, bucket string) (upHost string, err error) {
   290  	return getUpHost(p.cfg, 0, 0, ak, bucket)
   291  }
   292  
   293  func makeEndpointsFromUpHost(upHost string) region.EndpointsProvider {
   294  	if upHost != "" {
   295  		return &region.Endpoints{Preferred: []string{upHost}}
   296  	}
   297  	return nil
   298  }
   299  
   300  func makeApiOptionsFromUpEndpoints(upEndpoints region.EndpointsProvider) *apis.Options {
   301  	if upEndpoints != nil {
   302  		return &apis.Options{
   303  			OverwrittenEndpoints: upEndpoints,
   304  		}
   305  	}
   306  	return nil
   307  }
   308  
   309  func makeKeyForUploading(key string, hasKey bool) *string {
   310  	if hasKey {
   311  		return &key
   312  	} else {
   313  		return nil
   314  	}
   315  }
   316  
   317  type blkputRets []BlkputRet
   318  
   319  func (rets blkputRets) Len() int {
   320  	return len(rets)
   321  }
   322  
   323  func (rets blkputRets) Less(i, j int) bool {
   324  	return rets[i].blkIdx < rets[j].blkIdx
   325  }
   326  
   327  func (rets blkputRets) Swap(i, j int) {
   328  	rets[i], rets[j] = rets[j], rets[i]
   329  }
   330  
   331  type uploadPartInfos []UploadPartInfo
   332  
   333  func (infos uploadPartInfos) Len() int {
   334  	return len(infos)
   335  }
   336  
   337  func (infos uploadPartInfos) Less(i, j int) bool {
   338  	return infos[i].PartNumber < infos[j].PartNumber
   339  }
   340  
   341  func (infos uploadPartInfos) Swap(i, j int) {
   342  	infos[i], infos[j] = infos[j], infos[i]
   343  }