You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-library/vendor/github.com/qiniu/go-sdk/v7/storage/resume_uploader_apis.go

278 lines
9.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package storage
import (
"context"
"encoding/base64"
"github.com/qiniu/go-sdk/v7/internal/hostprovider"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/qiniu/go-sdk/v7/client"
"github.com/qiniu/go-sdk/v7/conf"
)
type resumeUploaderAPIs struct {
Client *client.Client
Cfg *Config
}
// BlkputRet 表示分片上传每个片上传完毕的返回值
type BlkputRet struct {
Ctx string `json:"ctx"`
Checksum string `json:"checksum"`
Crc32 uint32 `json:"crc32"`
Offset uint32 `json:"offset"`
Host string `json:"host"`
ExpiredAt int64 `json:"expired_at"`
chunkSize int
fileOffset int64
blkIdx int
}
func (p *resumeUploaderAPIs) mkBlk(ctx context.Context, upToken, upHost string, ret *BlkputRet, blockSize int64, body io.Reader, size int64) error {
reqUrl := upHost + "/mkblk/" + strconv.FormatInt(blockSize, 10)
return p.Client.CallWith64(ctx, ret, "POST", reqUrl, makeHeadersForUpload(upToken), body, size)
}
func (p *resumeUploaderAPIs) bput(ctx context.Context, upToken string, ret *BlkputRet, body io.Reader, size int64) error {
reqUrl := ret.Host + "/bput/" + ret.Ctx + "/" + strconv.FormatUint(uint64(ret.Offset), 10)
return p.Client.CallWith64(ctx, ret, "POST", reqUrl, makeHeadersForUpload(upToken), body, size)
}
// RputExtra 表示分片上传额外可以指定的参数
type RputExtra struct {
Recorder Recorder // 可选。上传进度记录
Params map[string]string // 可选。用户自定义参数,以"x:"开头,而且值不能为空,否则忽略
UpHost string
MimeType string // 可选。
ChunkSize int // 可选。每次上传的Chunk大小
TryTimes int // 可选。尝试次数
HostFreezeDuration time.Duration // 可选。主备域名冻结时间默认600s当一个域名请求失败单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
Progresses []BlkputRet // 可选。上传进度
Notify func(blkIdx int, blkSize int, ret *BlkputRet) // 可选。进度提示注意多个block是并行传输的
NotifyErr func(blkIdx int, blkSize int, err error)
}
func (extra *RputExtra) init() {
if extra.ChunkSize == 0 {
extra.ChunkSize = settings.ChunkSize
}
if extra.TryTimes == 0 {
extra.TryTimes = settings.TryTimes
}
if extra.HostFreezeDuration <= 0 {
extra.HostFreezeDuration = 10 * 60 * time.Second
}
if extra.Notify == nil {
extra.Notify = func(blkIdx, blkSize int, ret *BlkputRet) {}
}
if extra.NotifyErr == nil {
extra.NotifyErr = func(blkIdx, blkSize int, err error) {}
}
}
func (extra *RputExtra) getUpHost(useHttps bool) string {
return hostAddSchemeIfNeeded(useHttps, extra.UpHost)
}
func (p *resumeUploaderAPIs) mkfile(ctx context.Context, upToken, upHost string, ret interface{}, key string, hasKey bool, fsize int64, extra *RputExtra) (err error) {
url := upHost + "/mkfile/" + strconv.FormatInt(fsize, 10)
if extra == nil {
extra = &RputExtra{}
}
if extra.MimeType != "" {
url += "/mimeType/" + encode(extra.MimeType)
}
if hasKey {
url += "/key/" + encode(key)
}
for k, v := range extra.Params {
if (strings.HasPrefix(k, "x:") || strings.HasPrefix(k, "x-qn-meta-")) && v != "" {
url += "/" + k + "/" + encode(v)
}
}
ctxs := make([]string, len(extra.Progresses))
for i, progress := range extra.Progresses {
ctxs[i] = progress.Ctx
}
buf := strings.Join(ctxs, ",")
return p.Client.CallWith(ctx, ret, "POST", url, makeHeadersForUpload(upToken), strings.NewReader(buf), len(buf))
}
// InitPartsRet 表示分片上传 v2 初始化完毕的返回值
type InitPartsRet struct {
UploadID string `json:"uploadId"`
ExpireAt int64 `json:"expireAt"`
}
func (p *resumeUploaderAPIs) initParts(ctx context.Context, upToken, upHost, bucket, key string, hasKey bool, ret *InitPartsRet) error {
reqUrl := upHost + "/buckets/" + bucket + "/objects/" + encodeV2(key, hasKey) + "/uploads"
return p.Client.CallWith(ctx, ret, "POST", reqUrl, makeHeadersForUploadEx(upToken, ""), nil, 0)
}
// UploadPartsRet 表示分片上传 v2 每个片上传完毕的返回值
type UploadPartsRet struct {
Etag string `json:"etag"`
MD5 string `json:"md5"`
}
func (p *resumeUploaderAPIs) uploadParts(ctx context.Context, upToken, upHost, bucket, key string, hasKey bool, uploadId string, partNumber int64, partMD5 string, ret *UploadPartsRet, body io.Reader, size int64) error {
reqUrl := upHost + "/buckets/" + bucket + "/objects/" + encodeV2(key, hasKey) + "/uploads/" + uploadId + "/" + strconv.FormatInt(partNumber, 10)
return p.Client.CallWith64(ctx, ret, "PUT", reqUrl, makeHeadersForUploadPart(upToken, partMD5), body, size)
}
type UploadPartInfo struct {
Etag string `json:"etag"`
PartNumber int64 `json:"partNumber"`
partSize int
fileOffset int64
}
// RputV2Extra 表示分片上传 v2 额外可以指定的参数
type RputV2Extra struct {
Recorder Recorder // 可选。上传进度记录
Metadata map[string]string // 可选。用户自定义文件 metadata 信息
CustomVars map[string]string // 可选。用户自定义参数,以"x:"开头,而且值不能为空,否则忽略
UpHost string
MimeType string // 可选。
PartSize int64 // 可选。每次上传的块大小
TryTimes int // 可选。尝试次数
HostFreezeDuration time.Duration // 可选。主备域名冻结时间默认600s当一个域名请求失败单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
Progresses []UploadPartInfo // 上传进度
Notify func(partNumber int64, ret *UploadPartsRet) // 可选。进度提示注意多个block是并行传输的
NotifyErr func(partNumber int64, err error)
}
func (extra *RputV2Extra) init() {
if extra.PartSize == 0 {
extra.PartSize = settings.PartSize
}
if extra.TryTimes == 0 {
extra.TryTimes = settings.TryTimes
}
if extra.HostFreezeDuration <= 0 {
extra.HostFreezeDuration = 10 * 60 * time.Second
}
if extra.Notify == nil {
extra.Notify = func(partNumber int64, ret *UploadPartsRet) {}
}
if extra.NotifyErr == nil {
extra.NotifyErr = func(partNumber int64, err error) {}
}
}
func (extra *RputV2Extra) getUpHost(useHttps bool) string {
return hostAddSchemeIfNeeded(useHttps, extra.UpHost)
}
func hostAddSchemeIfNeeded(useHttps bool, host string) string {
if host == "" {
return ""
} else if strings.Contains(host, "://") {
return host
} else {
return endpoint(useHttps, host)
}
}
func (p *resumeUploaderAPIs) completeParts(ctx context.Context, upToken, upHost string, ret interface{}, bucket, key string, hasKey bool, uploadId string, extra *RputV2Extra) (err error) {
type CompletePartBody struct {
Parts []UploadPartInfo `json:"parts"`
MimeType string `json:"mimeType,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
CustomVars map[string]string `json:"customVars,omitempty"`
}
if extra == nil {
extra = &RputV2Extra{}
}
completePartBody := CompletePartBody{
Parts: extra.Progresses,
MimeType: extra.MimeType,
Metadata: extra.Metadata,
CustomVars: make(map[string]string),
}
for k, v := range extra.CustomVars {
if strings.HasPrefix(k, "x:") && v != "" {
completePartBody.CustomVars[k] = v
}
}
reqUrl := upHost + "/buckets/" + bucket + "/objects/" + encodeV2(key, hasKey) + "/uploads/" + uploadId
return p.Client.CallWithJson(ctx, ret, "POST", reqUrl, makeHeadersForUploadEx(upToken, conf.CONTENT_TYPE_JSON), &completePartBody)
}
func (p *resumeUploaderAPIs) upHost(ak, bucket string) (upHost string, err error) {
return getUpHost(p.Cfg, 0, 0, ak, bucket)
}
func (p *resumeUploaderAPIs) upHostProvider(ak, bucket string, retryMax int, hostFreezeDuration time.Duration) (hostProvider hostprovider.HostProvider, err error) {
return getUpHostProvider(p.Cfg, retryMax, hostFreezeDuration, ak, bucket)
}
func makeHeadersForUpload(upToken string) http.Header {
return makeHeadersForUploadEx(upToken, conf.CONTENT_TYPE_OCTET)
}
func makeHeadersForUploadPart(upToken, partMD5 string) http.Header {
headers := makeHeadersForUpload(upToken)
headers.Add("Content-MD5", partMD5)
return headers
}
func makeHeadersForUploadEx(upToken, contentType string) http.Header {
headers := http.Header{}
if contentType != "" {
headers.Add("Content-Type", contentType)
}
headers.Add("Authorization", "UpToken "+upToken)
return headers
}
func encode(raw string) string {
return base64.URLEncoding.EncodeToString([]byte(raw))
}
func encodeV2(key string, hasKey bool) string {
if !hasKey {
return "~"
} else {
return encode(key)
}
}
type blkputRets []BlkputRet
func (rets blkputRets) Len() int {
return len(rets)
}
func (rets blkputRets) Less(i, j int) bool {
return rets[i].blkIdx < rets[j].blkIdx
}
func (rets blkputRets) Swap(i, j int) {
rets[i], rets[j] = rets[j], rets[i]
}
type uploadPartInfos []UploadPartInfo
func (infos uploadPartInfos) Len() int {
return len(infos)
}
func (infos uploadPartInfos) Less(i, j int) bool {
return infos[i].PartNumber < infos[j].PartNumber
}
func (infos uploadPartInfos) Swap(i, j int) {
infos[i], infos[j] = infos[j], infos[i]
}