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  }