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

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"path"
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/qiniu/go-sdk/v7/client"
    12  	"github.com/qiniu/go-sdk/v7/internal/clientv2"
    13  	"github.com/qiniu/go-sdk/v7/storagev2/http_client"
    14  	"github.com/qiniu/go-sdk/v7/storagev2/region"
    15  	"github.com/qiniu/go-sdk/v7/storagev2/uploader"
    16  	"github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    17  )
    18  
    19  // PutExtra 为表单上传的额外可选项
    20  type PutExtra struct {
    21  	// 可选。
    22  	// 用户自定义参数:key 以"x:"开头,而且 value 不能为空 eg: key为x:qqq
    23  	// 自定义 meta:key 以"x-qn-meta-"开头,而且 value 不能为空 eg: key为x-qn-meta-aaa
    24  	Params map[string]string
    25  
    26  	UpHost string
    27  
    28  	TryTimes int // 可选。尝试次数
    29  
    30  	// 主备域名冻结时间(默认:600s),当一个域名请求失败(单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
    31  	HostFreezeDuration time.Duration
    32  
    33  	// 可选,当为 "" 时候,服务端自动判断。
    34  	MimeType string
    35  
    36  	// 上传事件:进度通知。这个事件的回调函数应该尽可能快地结束。
    37  	OnProgress func(fsize, uploaded int64)
    38  }
    39  
    40  func (extra *PutExtra) init() {
    41  	if extra.TryTimes == 0 {
    42  		extra.TryTimes = settings.TryTimes
    43  	}
    44  	if extra.HostFreezeDuration <= 0 {
    45  		extra.HostFreezeDuration = 10 * 60 * time.Second
    46  	}
    47  }
    48  
    49  // PutRet 为七牛标准的上传回复内容。
    50  // 如果使用了上传回调或者自定义了returnBody,那么需要根据实际情况,自己自定义一个返回值结构体
    51  type PutRet struct {
    52  	Hash         string `json:"hash"`
    53  	PersistentID string `json:"persistentId"`
    54  	Key          string `json:"key"`
    55  }
    56  
    57  // FormUploader 表示一个表单上传的对象
    58  type FormUploader struct {
    59  	// Deprecated
    60  	Client *client.Client
    61  	// Deprecated
    62  	Cfg      *Config
    63  	uploader uploader.Uploader
    64  }
    65  
    66  // NewFormUploader 用来构建一个表单上传的对象
    67  func NewFormUploader(cfg *Config) *FormUploader {
    68  	return NewFormUploaderEx(cfg, nil)
    69  }
    70  
    71  // NewFormUploaderEx 用来构建一个表单上传的对象
    72  func NewFormUploaderEx(cfg *Config, clt *client.Client) *FormUploader {
    73  	if cfg == nil {
    74  		cfg = NewConfig()
    75  	}
    76  
    77  	if clt == nil {
    78  		clt = &client.DefaultClient
    79  	}
    80  
    81  	bucketQuery, _ := region.NewBucketRegionsQuery(getUcEndpoint(cfg.UseHTTPS, nil), &region.BucketRegionsQueryOptions{
    82  		UseInsecureProtocol: !cfg.UseHTTPS,
    83  		Client:              clt.Client,
    84  	})
    85  
    86  	opts := http_client.Options{
    87  		BasicHTTPClient:     clt.Client,
    88  		UseInsecureProtocol: !cfg.UseHTTPS,
    89  		BucketQuery:         bucketQuery,
    90  		AccelerateUploading: cfg.AccelerateUploading,
    91  		HostRetryConfig:     &clientv2.RetryConfig{},
    92  	}
    93  	if region := cfg.GetRegion(); region != nil {
    94  		opts.Regions = region
    95  	}
    96  
    97  	return &FormUploader{
    98  		Client:   clt,
    99  		Cfg:      cfg,
   100  		uploader: uploader.NewFormUploader(&uploader.FormUploaderOptions{Options: opts}),
   101  	}
   102  }
   103  
   104  // PutFile 用来以表单方式上传一个文件,和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。
   105  //
   106  // ctx       是请求的上下文。
   107  // ret       是上传成功后返回的数据。如果 uptoken 中没有设置 callbackUrl 或 returnBody,那么返回的数据结构是 PutRet 结构。
   108  // uptoken   是由业务服务器颁发的上传凭证。
   109  // key       是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。
   110  // localFile 是要上传的文件的本地路径。
   111  // extra     是上传的一些可选项,可以指定为nil。详细见 PutExtra 结构的描述。
   112  func (p *FormUploader) PutFile(
   113  	ctx context.Context, ret interface{}, uptoken, key, localFile string, extra *PutExtra,
   114  ) (err error) {
   115  	return p.putFile(ctx, ret, uptoken, key, true, localFile, extra)
   116  }
   117  
   118  // PutFileWithoutKey 用来以表单方式上传一个文件。不指定文件上传后保存的key的情况下,文件命名方式首先看看
   119  // uptoken 中是否设置了 saveKey,如果设置了 saveKey,那么按 saveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。
   120  // 和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。
   121  //
   122  // ctx       是请求的上下文。
   123  // ret       是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。
   124  // uptoken   是由业务服务器颁发的上传凭证。
   125  // localFile 是要上传的文件的本地路径。
   126  // extra     是上传的一些可选项。可以指定为nil。详细见 PutExtra 结构的描述。
   127  func (p *FormUploader) PutFileWithoutKey(
   128  	ctx context.Context, ret interface{}, uptoken, localFile string, extra *PutExtra,
   129  ) (err error) {
   130  	return p.putFile(ctx, ret, uptoken, "", false, localFile, extra)
   131  }
   132  
   133  func (p *FormUploader) putFile(
   134  	ctx context.Context, ret interface{}, upToken string,
   135  	key string, hasKey bool, localFile string, extra *PutExtra,
   136  ) error {
   137  	if extra == nil {
   138  		extra = &PutExtra{}
   139  	}
   140  	extra.init()
   141  
   142  	objectParams := p.newObjectParams(upToken, key, hasKey, extra)
   143  	objectParams.FileName = filepath.Base(localFile)
   144  	return p.uploader.UploadFile(ctx, localFile, objectParams, ret)
   145  }
   146  
   147  // Put 用来以表单方式上传一个文件。
   148  //
   149  // ctx     是请求的上下文。
   150  // ret     是上传成功后返回的数据。如果 uptoken 中没有设置 callbackUrl 或 returnBody,那么返回的数据结构是 PutRet 结构。
   151  // uptoken 是由业务服务器颁发的上传凭证。
   152  // key     是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。
   153  // data    是文件内容的访问接口(io.Reader)。
   154  // fsize   是要上传的文件大小。
   155  // extra   是上传的一些可选项。可以指定为nil。详细见 PutExtra 结构的描述。
   156  func (p *FormUploader) Put(
   157  	ctx context.Context, ret interface{}, uptoken, key string, data io.Reader, size int64, extra *PutExtra,
   158  ) (err error) {
   159  	err = p.put(ctx, ret, uptoken, key, true, data, size, extra, path.Base(key))
   160  	return
   161  }
   162  
   163  // PutWithoutKey 用来以表单方式上传一个文件。不指定文件上传后保存的key的情况下,文件命名方式首先看看 uptoken 中是否设置了 saveKey,
   164  // 如果设置了 saveKey,那么按 saveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。
   165  //
   166  // ctx     是请求的上下文。
   167  // ret     是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。
   168  // uptoken 是由业务服务器颁发的上传凭证。
   169  // data    是文件内容的访问接口(io.Reader)。
   170  // fsize   是要上传的文件大小。
   171  // extra   是上传的一些可选项。详细见 PutExtra 结构的描述。
   172  func (p *FormUploader) PutWithoutKey(
   173  	ctx context.Context, ret interface{}, uptoken string, data io.Reader, size int64, extra *PutExtra,
   174  ) (err error) {
   175  	err = p.put(ctx, ret, uptoken, "", false, data, size, extra, "")
   176  	return err
   177  }
   178  
   179  func (p *FormUploader) put(
   180  	ctx context.Context, ret interface{}, upToken string,
   181  	key string, hasKey bool, data io.Reader, size int64, extra *PutExtra, fileName string,
   182  ) error {
   183  	if extra == nil {
   184  		extra = &PutExtra{}
   185  	}
   186  	extra.init()
   187  
   188  	objectParams := p.newObjectParams(upToken, key, hasKey, extra)
   189  	objectParams.FileName = "Untitled"
   190  
   191  	return p.uploader.UploadReader(ctx, data, objectParams, ret)
   192  }
   193  
   194  func (p *FormUploader) newObjectParams(upToken string, key string, hasKey bool, extra *PutExtra) *uploader.ObjectOptions {
   195  	objectParams := uploader.ObjectOptions{
   196  		ContentType: extra.MimeType,
   197  		UpToken:     uptoken.NewParser(upToken),
   198  	}
   199  	if hasKey {
   200  		objectParams.ObjectName = &key
   201  	}
   202  	if extra.UpHost != "" {
   203  		objectParams.RegionsProvider = &region.Region{
   204  			Up: region.Endpoints{Preferred: []string{extra.UpHost}},
   205  		}
   206  	} else if p.Cfg.UpHost != "" {
   207  		objectParams.RegionsProvider = &region.Region{
   208  			Up: region.Endpoints{Preferred: []string{p.Cfg.UpHost}},
   209  		}
   210  	} else if region := p.Cfg.GetRegion(); region != nil {
   211  		objectParams.RegionsProvider = region
   212  	}
   213  	objectParams.Metadata, objectParams.CustomVars = splitParams(extra.Params)
   214  	if extra.OnProgress != nil {
   215  		objectParams.OnUploadingProgress = func(progress *uploader.UploadingProgress) {
   216  			extra.OnProgress(int64(progress.TotalSize), int64(progress.Uploaded))
   217  		}
   218  	}
   219  	return &objectParams
   220  }
   221  
   222  // Deprecated
   223  func (p *FormUploader) UpHost(ak, bucket string) (upHost string, err error) {
   224  	return getUpHost(p.Cfg, 0, 0, ak, bucket)
   225  }
   226  
   227  func makeCustomData(params map[string]string) map[string]string {
   228  	customData := make(map[string]string, len(params))
   229  	for k, v := range params {
   230  		if (strings.HasPrefix(k, "x:") || strings.HasPrefix(k, "x-qn-meta-")) && v != "" {
   231  			customData[k] = v
   232  		}
   233  	}
   234  	return customData
   235  }
   236  
   237  func splitParams(params map[string]string) (metadata, customVars map[string]string) {
   238  	metadata = make(map[string]string)
   239  	customVars = make(map[string]string)
   240  	for k, v := range params {
   241  		if v == "" {
   242  			continue
   243  		}
   244  		if strings.HasPrefix(k, "x:") {
   245  			customVars[k] = v
   246  		} else if strings.HasPrefix(k, "x-qn-meta-") {
   247  			metadata[k] = v
   248  		}
   249  	}
   250  	return metadata, customVars
   251  }