github.com/qiniu/go-sdk/v7@v7.27.0/storage/base64_upload.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "fmt" 8 "hash/crc32" 9 "io" 10 "net/http" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/qiniu/go-sdk/v7/conf" 16 "github.com/qiniu/go-sdk/v7/internal/hostprovider" 17 18 "github.com/qiniu/go-sdk/v7/client" 19 ) 20 21 // Base64Uploader 表示一个Base64上传对象 22 type Base64Uploader struct { 23 client *client.Client 24 cfg *Config 25 } 26 27 // NewBase64Uploader 用来构建一个Base64上传的对象 28 func NewBase64Uploader(cfg *Config) *Base64Uploader { 29 return NewBase64UploaderEx(cfg, nil) 30 } 31 32 // NewBase64UploaderEx 用来构建一个Base64上传的对象 33 func NewBase64UploaderEx(cfg *Config, clt *client.Client) *Base64Uploader { 34 if cfg == nil { 35 cfg = NewConfig() 36 } 37 38 if clt == nil { 39 clt = &client.DefaultClient 40 } 41 42 return &Base64Uploader{ 43 client: clt, 44 cfg: cfg, 45 } 46 } 47 48 // Base64PutExtra 为Base64上传的额外可选项 49 type Base64PutExtra struct { 50 // 可选,用户自定义参数,必须以 "x:" 开头。若不以x:开头,则忽略。 51 Params map[string]string 52 53 // 可选,当为 "" 时候,服务端自动判断。 54 MimeType string 55 56 TryTimes int // 可选。尝试次数 57 58 // 主备域名冻结时间(默认:600s),当一个域名请求失败(单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。 59 HostFreezeDuration time.Duration 60 } 61 62 func (r *Base64PutExtra) init() { 63 if r.TryTimes == 0 { 64 r.TryTimes = settings.TryTimes 65 } 66 if r.HostFreezeDuration <= 0 { 67 r.HostFreezeDuration = 10 * 60 * time.Second 68 } 69 } 70 71 // Put 用来以Base64方式上传一个文件 72 // 73 // ctx 是请求的上下文。 74 // ret 是上传成功后返回的数据。如果 uptoken 中没有设置 callbackUrl 或 returnBody,那么返回的数据结构是 PutRet 结构。 75 // uptoken 是由业务服务器颁发的上传凭证。 76 // key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 77 // base64Data 是要上传的Base64数据,一般为图片数据的Base64编码字符串 78 // extra 是上传的一些可选项,可以指定为nil。详细见 Base64PutExtra 结构的描述。 79 func (p *Base64Uploader) Put( 80 ctx context.Context, ret interface{}, uptoken, key string, base64Data []byte, extra *Base64PutExtra, 81 ) (err error) { 82 return p.put(ctx, ret, uptoken, key, true, base64Data, extra) 83 } 84 85 // PutWithoutKey 用来以Base64方式上传一个文件,保存的文件名以文件的内容hash作为文件名 86 func (p *Base64Uploader) PutWithoutKey( 87 ctx context.Context, ret interface{}, uptoken string, base64Data []byte, extra *Base64PutExtra, 88 ) (err error) { 89 return p.put(ctx, ret, uptoken, "", false, base64Data, extra) 90 } 91 92 func (p *Base64Uploader) put( 93 ctx context.Context, ret interface{}, uptoken, key string, hasKey bool, base64Data []byte, extra *Base64PutExtra, 94 ) (err error) { 95 // set default extra 96 if extra == nil { 97 extra = &Base64PutExtra{} 98 } 99 extra.init() 100 101 // calc crc32 102 h := crc32.NewIEEE() 103 rawReader := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(base64Data)) 104 fsize, decodeErr := io.Copy(h, rawReader) 105 if decodeErr != nil { 106 err = fmt.Errorf("invalid base64 data, %s", decodeErr.Error()) 107 return 108 } 109 fCrc32 := h.Sum32() 110 111 postPath := bytes.NewBufferString("/putb64") 112 // add fsize 113 postPath.WriteString("/") 114 postPath.WriteString(strconv.Itoa(int(fsize))) 115 116 // add key 117 if hasKey { 118 postPath.WriteString("/key/") 119 postPath.WriteString(base64.URLEncoding.EncodeToString([]byte(key))) 120 } 121 // add mimeType 122 if extra.MimeType != "" { 123 postPath.WriteString("/mimeType/") 124 postPath.WriteString(base64.URLEncoding.EncodeToString([]byte(extra.MimeType))) 125 } 126 127 // add crc32 128 postPath.WriteString("/crc32/") 129 postPath.WriteString(fmt.Sprintf("%d", fCrc32)) 130 131 // add extra params 132 if len(extra.Params) > 0 { 133 for k, v := range extra.Params { 134 if strings.HasPrefix(k, "x:") && v != "" { 135 postPath.WriteString("/") 136 postPath.WriteString(k) 137 postPath.WriteString("/") 138 postPath.WriteString(base64.URLEncoding.EncodeToString([]byte(v))) 139 } 140 } 141 } 142 143 // get up host 144 ak, bucket, gErr := getAkBucketFromUploadToken(uptoken) 145 if gErr != nil { 146 err = gErr 147 return 148 } 149 150 var upHostProvider hostprovider.HostProvider 151 upHostProvider, err = p.upHostProvider(ak, bucket, extra) 152 if err != nil { 153 return 154 } 155 156 return doUploadAction(upHostProvider, extra.TryTimes, extra.HostFreezeDuration, func(host string) error { 157 postURL := fmt.Sprintf("%s%s", host, postPath.String()) 158 headers := http.Header{} 159 headers.Set("Content-Type", conf.CONTENT_TYPE_OCTET) 160 headers.Set("Authorization", "UpToken "+uptoken) 161 162 return p.client.CallWith(ctx, ret, "POST", postURL, headers, bytes.NewReader(base64Data), len(base64Data)) 163 }) 164 } 165 166 func (p *Base64Uploader) upHostProvider(ak, bucket string, extra *Base64PutExtra) (hostProvider hostprovider.HostProvider, err error) { 167 return getUpHostProvider(p.cfg, extra.TryTimes, extra.HostFreezeDuration, ak, bucket) 168 }