Browse Source

add CI QRCode && Update CI

master
jojoliang 4 years ago
parent
commit
e2bf821180
  1. 21
      auth.go
  2. 175
      ci.go
  3. 7
      ci_doc.go
  4. 27
      example/CI/ci_QRcode.go
  5. 15
      example/CI/ci_get.go
  6. 7
      example/CI/content_auditing/ci_image_recognition.go
  7. 132
      example/object/ci_doc_process.go
  8. 71
      example/object/ci_video_auditing_job.go
  9. 7
      object.go
  10. 1
      object_part.go

21
auth.go

@ -48,6 +48,12 @@ var needSignHeaders = map[string]bool{
"x-cos-object-type": true,
}
var ciParameters = map[string]bool{
"imagemogr2/": true,
"watermark/": true,
"imageview2/": true,
}
func safeURLEncode(s string) string {
s = encodeURIComponent(s)
s = strings.Replace(s, "!", "%21", -1)
@ -205,8 +211,10 @@ func genFormatParameters(parameters url.Values) (formatParameters string, signed
for key, values := range parameters {
key = strings.ToLower(key)
for _, value := range values {
ps.Add(key, value)
signedParameterList = append(signedParameterList, key)
if !isCIParameter(key) {
ps.Add(key, value)
signedParameterList = append(signedParameterList, key)
}
}
}
//formatParameters = strings.ToLower(ps.Encode())
@ -246,6 +254,15 @@ func calHMACDigest(key, msg, signMethod string) []byte {
return h.Sum(nil)
}
func isCIParameter(key string) bool {
for k, v := range ciParameters {
if strings.HasPrefix(key, k) && v {
return true
}
}
return false
}
func isSignHeader(key string) bool {
for k, v := range needSignHeaders {
if key == k && v {

175
ci.go

@ -1,14 +1,18 @@
package cos
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"hash/crc64"
"io"
"net/http"
"os"
"strconv"
)
type CIService service
@ -54,15 +58,24 @@ type PicImageInfo struct {
Orientation int `xml:"Orientation,omitempty"`
}
type PicProcessObject struct {
Key string `xml:"Key,omitempty"`
Location string `xml:"Location,omitempty"`
Format string `xml:"Format,omitempty"`
Width int `xml:"Width,omitempty"`
Height int `xml:"Height,omitempty"`
Size int `xml:"Size,omitempty"`
Quality int `xml:"Quality,omitempty"`
ETag string `xml:"ETag,omitempty"`
WatermarkStatus int `xml:"WatermarkStatus,omitempty"`
Key string `xml:"Key,omitempty"`
Location string `xml:"Location,omitempty"`
Format string `xml:"Format,omitempty"`
Width int `xml:"Width,omitempty"`
Height int `xml:"Height,omitempty"`
Size int `xml:"Size,omitempty"`
Quality int `xml:"Quality,omitempty"`
ETag string `xml:"ETag,omitempty"`
WatermarkStatus int `xml:"WatermarkStatus,omitempty"`
CodeStatus int `xml:"CodeStatus,omitempty"`
QRcodeInfo []QRcodeInfo `xml:"QRcodeInfo,omitempty"`
}
type QRcodeInfo struct {
CodeUrl string `xml:"CodeUrl,omitempty"`
CodeLocation *CodeLocation `xml:"CodeLocation,omitempty"`
}
type CodeLocation struct {
Point []string `xml:"Point,omitempty"`
}
type picOperationsHeader struct {
@ -110,9 +123,10 @@ type RecognitionInfo struct {
}
// 图片审核 https://cloud.tencent.com/document/product/460/37318
func (s *CIService) ImageRecognition(ctx context.Context, name string, opt *ImageRecognitionOptions) (*ImageRecognitionResult, *Response, error) {
if opt != nil && opt.CIProcess == "" {
opt.CIProcess = "sensitive-content-recognition"
func (s *CIService) ImageRecognition(ctx context.Context, name string, DetectType string) (*ImageRecognitionResult, *Response, error) {
opt := &ImageRecognitionOptions{
CIProcess: "sensitive-content-recognition",
DetectType: DetectType,
}
var res ImageRecognitionResult
sendOpt := sendOptions{
@ -153,6 +167,7 @@ type PutVideoAuditingJobResult struct {
} `xml:"JobsDetail,omitempty"`
}
// 视频审核-创建任务 https://cloud.tencent.com/document/product/460/46427
func (s *CIService) PutVideoAuditingJob(ctx context.Context, opt *PutVideoAuditingJobOptions) (*PutVideoAuditingJobResult, *Response, error) {
var res PutVideoAuditingJobResult
sendOpt := sendOptions{
@ -194,6 +209,7 @@ type GetVideoAuditingJobSnapshot struct {
AdsInfo *RecognitionInfo `xml:",omitempty"`
}
// 视频审核-查询任务 https://cloud.tencent.com/document/product/460/46926
func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*GetVideoAuditingJobResult, *Response, error) {
var res GetVideoAuditingJobResult
sendOpt := sendOptions{
@ -206,7 +222,8 @@ func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*Get
return &res, resp, err
}
// ci put https://cloud.tencent.com/document/product/460/18147
// 图片持久化处理-上传时处理 https://cloud.tencent.com/document/product/460/18147
// 二维码识别-上传时识别 https://cloud.tencent.com/document/product/460/37513
func (s *CIService) Put(ctx context.Context, name string, r io.Reader, uopt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
if r == nil {
return nil, nil, fmt.Errorf("reader is nil")
@ -257,3 +274,135 @@ func (s *CIService) PutFromFile(ctx context.Context, name string, filePath strin
return s.Put(ctx, name, fd, opt)
}
// 基本图片处理 https://cloud.tencent.com/document/product/460/36540
// 盲水印-下载时添加 https://cloud.tencent.com/document/product/460/19017
func (s *CIService) Get(ctx context.Context, name string, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
var u string
if len(id) == 1 {
u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], operation)
} else if len(id) == 0 {
u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), operation)
} else {
return nil, errors.New("wrong params")
}
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: u,
method: http.MethodGet,
optQuery: opt,
optHeader: opt,
disableCloseBody: true,
}
resp, err := s.client.send(ctx, &sendOpt)
if opt != nil && opt.Listener != nil {
if err == nil && resp != nil {
if totalBytes, e := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); e == nil {
resp.Body = TeeReader(resp.Body, nil, totalBytes, opt.Listener)
}
}
}
return resp, err
}
func (s *CIService) GetToFile(ctx context.Context, name, localpath, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
resp, err := s.Get(ctx, name, operation, opt, id...)
if err != nil {
return resp, err
}
defer resp.Body.Close()
// If file exist, overwrite it
fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
return resp, err
}
_, err = io.Copy(fd, resp.Body)
fd.Close()
if err != nil {
return resp, err
}
return resp, nil
}
type GetQRcodeResult struct {
XMLName xml.Name `xml:"Response"`
CodeStatus int `xml:"CodeStatus,omitempty"`
QRcodeInfo *QRcodeInfo `xml:"QRcodeInfo,omitempty"`
ResultImage string `xml:"ResultImage,omitempty"`
}
// 二维码识别-下载时识别 https://cloud.tencent.com/document/product/436/54070
func (s *CIService) GetQRcode(ctx context.Context, name string, cover int, opt *ObjectGetOptions, id ...string) (*GetQRcodeResult, *Response, error) {
var u string
if len(id) == 1 {
u = fmt.Sprintf("/%s?versionId=%s&ci-process=QRcode&cover=%v", encodeURIComponent(name), id[0], cover)
} else if len(id) == 0 {
u = fmt.Sprintf("/%s?ci-process=QRcode&cover=%v", encodeURIComponent(name), cover)
} else {
return nil, nil, errors.New("wrong params")
}
var res GetQRcodeResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: u,
method: http.MethodGet,
optQuery: opt,
optHeader: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type GenerateQRcodeOptions struct {
QRcodeContent string `url:"qrcode-content,omitempty"`
Mode int `url:"mode,omitempty"`
Width int `url:"width,omitempty"`
}
type GenerateQRcodeResult struct {
XMLName xml.Name `xml:"Response"`
ResultImage string `xml:"ResultImage,omitempty"`
}
// 二维码生成 https://cloud.tencent.com/document/product/436/54071
func (s *CIService) GenerateQRcode(ctx context.Context, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
var res GenerateQRcodeResult
sendOpt := &sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: "/?ci-process=qrcode-generate",
method: http.MethodGet,
optQuery: opt,
result: &res,
}
resp, err := s.client.send(ctx, sendOpt)
return &res, resp, err
}
func (s *CIService) GenerateQRcodeToFile(ctx context.Context, filePath string, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
res, resp, err := s.GenerateQRcode(ctx, opt)
if err != nil {
return res, resp, err
}
// If file exist, overwrite it
fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
return res, resp, err
}
defer fd.Close()
bs, err := base64.StdEncoding.DecodeString(res.ResultImage)
if err != nil {
return res, resp, err
}
fb := bytes.NewReader(bs)
_, err = io.Copy(fd, fb)
return res, resp, err
}

7
ci_doc.go

@ -73,6 +73,7 @@ type CreateDocProcessJobsResult struct {
JobsDetail DocProcessJobDetail `xml:"JobsDetail,omitempty"`
}
// 创建文档预览任务 https://cloud.tencent.com/document/product/436/54056
func (s *CIService) CreateDocProcessJobs(ctx context.Context, opt *CreateDocProcessJobsOptions) (*CreateDocProcessJobsResult, *Response, error) {
var res CreateDocProcessJobsResult
sendOpt := sendOptions{
@ -92,6 +93,7 @@ type DescribeDocProcessJobResult struct {
NonExistJobIds string `xml:"NonExistJobIds,omitempty"`
}
// 查询文档预览任务 https://cloud.tencent.com/document/product/436/54095
func (s *CIService) DescribeDocProcessJob(ctx context.Context, jobid string) (*DescribeDocProcessJobResult, *Response, error) {
var res DescribeDocProcessJobResult
sendOpt := sendOptions{
@ -121,6 +123,7 @@ type DescribeDocProcessJobsResult struct {
NextToken string `xml:"NextToken,omitempty"`
}
// 拉取符合条件的文档预览任务 https://cloud.tencent.com/document/product/436/54096
func (s *CIService) DescribeDocProcessJobs(ctx context.Context, opt *DescribeDocProcessJobsOptions) (*DescribeDocProcessJobsResult, *Response, error) {
var res DescribeDocProcessJobsResult
sendOpt := sendOptions{
@ -169,6 +172,7 @@ type DocProcessQueueNotifyConfig struct {
Event string `xml:"Event,omitempty"`
}
// 查询文档预览队列 https://cloud.tencent.com/document/product/436/54055
func (s *CIService) DescribeDocProcessQueues(ctx context.Context, opt *DescribeDocProcessQueuesOptions) (*DescribeDocProcessQueuesResult, *Response, error) {
var res DescribeDocProcessQueuesResult
sendOpt := sendOptions{
@ -196,6 +200,7 @@ type UpdateDocProcessQueueResult struct {
Queue *DocProcessQueue `xml:"Queue"`
}
// 更新文档预览队列 https://cloud.tencent.com/document/product/436/54094
func (s *CIService) UpdateDocProcessQueue(ctx context.Context, opt *UpdateDocProcessQueueOptions) (*UpdateDocProcessQueueResult, *Response, error) {
var res UpdateDocProcessQueueResult
sendOpt := sendOptions{
@ -233,6 +238,7 @@ type DocProcessBucket struct {
AliasBucketId string `xml:"AliasBucketId,omitempty"`
}
// 查询文档预览开通状态 https://cloud.tencent.com/document/product/436/54057
func (s *CIService) DescribeDocProcessBuckets(ctx context.Context, opt *DescribeDocProcessBucketsOptions) (*DescribeDocProcessBucketsResult, *Response, error) {
var res DescribeDocProcessBucketsResult
sendOpt := sendOptions{
@ -259,6 +265,7 @@ type DocPreviewOptions struct {
Zoom int `url:"zoom,omitempty"`
}
// 同步请求接口 https://cloud.tencent.com/document/product/436/54058
func (s *CIService) DocPreview(ctx context.Context, name string, opt *DocPreviewOptions) (*Response, error) {
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,

27
example/object/ci_put.go → example/CI/ci_QRcode.go

@ -42,7 +42,7 @@ func main() {
// Notice when put a large file and set need the request body, might happend out of memory error.
RequestBody: false,
ResponseHeader: true,
ResponseBody: true,
ResponseBody: false,
},
},
})
@ -58,13 +58,32 @@ func main() {
Rules: []cos.PicOperationsRules{
{
FileId: "format.jpg",
Rule: "imageView2/format/png",
Rule: "QRcode/cover/1",
},
},
}
opt.XOptionHeader.Add("Pic-Operations", cos.EncodePicOperations(pic))
name := "test.jpg"
local_filename := "./test.jpg"
_, err := c.Object.PutFromFile(context.Background(), name, local_filename, opt)
local_filename := "./QRcode.jpg"
res, _, err := c.CI.PutFromFile(context.Background(), name, local_filename, opt)
log_status(err)
fmt.Printf("%+v\n", res)
fmt.Printf("%+v\n", res.OriginalInfo)
fmt.Printf("%+v\n", res.ProcessResults)
res2, _, err := c.CI.GetQRcode(context.Background(), name, 0, nil)
log_status(err)
fmt.Printf("%+v\n", res2)
gopt := &cos.GenerateQRcodeOptions{
QRcodeContent: fmt.Sprintf("<%v>", res2.QRcodeInfo.CodeUrl),
Mode: 0,
Width: 200,
}
res3, _, err := c.CI.GenerateQRcode(context.Background(), gopt)
log_status(err)
fmt.Printf("%+v\n", res3)
_, _, err = c.CI.GenerateQRcodeToFile(context.Background(), "./downQRcode.jpg", gopt)
log_status(err)
}

15
example/object/ci_image_process.go → example/CI/ci_get.go

@ -42,22 +42,13 @@ func main() {
// Notice when put a large file and set need the request body, might happend out of memory error.
RequestBody: false,
ResponseHeader: true,
ResponseBody: true,
ResponseBody: false,
},
},
})
opt := &cos.ImageProcessOptions{
IsPicInfo: 1,
Rules: []cos.PicOperationsRules{
{
FileId: "format.jpg",
Rule: "imageView2/format/png",
},
},
}
name := "test.jpg"
res, _, err := c.CI.ImageProcess(context.Background(), name, opt)
filepath := "test.jpg"
_, err := c.CI.GetToFile(context.Background(), name, filepath, "imageMogr2/thumbnail/!50px", nil)
log_status(err)
fmt.Printf("%+v\n", res)
}

7
example/object/ci_image_recognition.go → example/CI/content_auditing/ci_image_recognition.go

@ -45,12 +45,9 @@ func main() {
},
},
})
opt := &cos.ImageRecognitionOptions{
DetectType: "porn,terrorist,politics",
}
DetectType := "porn,terrorist,politics"
name := "test.jpg"
res, _, err := c.CI.ImageRecognition(context.Background(), name, opt)
res, _, err := c.CI.ImageRecognition(context.Background(), name, DetectType)
log_status(err)
fmt.Printf("%+v\n", res)
}

132
example/object/ci_doc_process.go

@ -1,132 +0,0 @@
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"github.com/tencentyun/cos-go-sdk-v5"
"github.com/tencentyun/cos-go-sdk-v5/debug"
)
func log_status(err error) {
if err == nil {
return
}
if cos.IsNotFoundError(err) {
// WARN
fmt.Println("WARN: Resource is not existed")
} else if e, ok := cos.IsCOSError(err); ok {
fmt.Printf("ERROR: Code: %v\n", e.Code)
fmt.Printf("ERROR: Message: %v\n", e.Message)
fmt.Printf("ERROR: Resource: %v\n", e.Resource)
fmt.Printf("ERROR: RequestId: %v\n", e.RequestID)
// ERROR
} else {
fmt.Printf("ERROR: %v\n", err)
// ERROR
}
}
func main() {
u, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com")
cu, _ := url.Parse("https://test-1259654469.ci.ap-guangzhou.myqcloud.com")
b := &cos.BaseURL{BucketURL: u, CIURL: cu}
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: os.Getenv("COS_SECRETID"),
SecretKey: os.Getenv("COS_SECRETKEY"),
Transport: &debug.DebugRequestTransport{
RequestHeader: true,
// Notice when put a large file and set need the request body, might happend out of memory error.
RequestBody: true,
ResponseHeader: true,
ResponseBody: true,
},
},
})
// 1、UpdateDocProcessQueue
updateQueueOpt := &cos.UpdateDocProcessQueueOptions{
Name: "queue-doc-process-1",
QueueID: "p111a8dd208104ce3b11c78398f658ca8",
State: "Active",
NotifyConfig: &cos.DocProcessQueueNotifyConfig{
State: "Off",
},
}
updateQueueRes, _, err := c.CI.UpdateDocProcessQueue(context.Background(), updateQueueOpt)
log_status(err)
fmt.Printf("%+v\n", updateQueueRes)
// 2、DescribeDocProcessQueues
DescribeQueueOpt := &cos.DescribeDocProcessQueuesOptions{
QueueIds: "p111a8dd208104ce3b11c78398f658ca8,p4318f85d2aa14c43b1dba6f9b78be9b3,aacb2bb066e9c4478834d4196e76c49d3",
PageNumber: 1,
PageSize: 2,
}
DescribeQueueRes, _, err := c.CI.DescribeDocProcessQueues(context.Background(), DescribeQueueOpt)
log_status(err)
fmt.Printf("%+v\n", DescribeQueueRes)
// 3、DescribeDocProcessBuckets
BucketsOpt := &cos.DescribeDocProcessBucketsOptions{
Regions: "All",
}
BucketsRes, _, err := c.CI.DescribeDocProcessBuckets(context.Background(), BucketsOpt)
log_status(err)
fmt.Printf("%+v\n", BucketsRes)
// 4、CreateDocProcessJobs
createJobOpt := &cos.CreateDocProcessJobsOptions{
Tag: "DocProcess",
Input: &cos.DocProcessJobInput{
Object: "form.pdf",
},
Operation: &cos.DocProcessJobOperation{
Output: &cos.DocProcessJobOutput{
Region: "ap-guangzhou",
Object: "test-doc${Number}",
Bucket: "test-1259654469",
},
DocProcess: &cos.DocProcessJobDocProcess{
TgtType: "png",
StartPage: 1,
EndPage: -1,
ImageParams: "watermark/1/image/aHR0cDovL3Rlc3QwMDUtMTI1MTcwNDcwOC5jb3MuYXAtY2hvbmdxaW5nLm15cWNsb3VkLmNvbS8xLmpwZw==/gravity/southeast",
},
},
QueueId: "p111a8dd208104ce3b11c78398f658ca8",
}
createJobRes, _, err := c.CI.CreateDocProcessJobs(context.Background(), createJobOpt)
log_status(err)
fmt.Printf("%+v\n", createJobRes.JobsDetail)
// 5、DescribeDocProcessJob
DescribeJobRes, _, err := c.CI.DescribeDocProcessJob(context.Background(), createJobRes.JobsDetail.JobId)
log_status(err)
fmt.Printf("%+v\n", DescribeJobRes.JobsDetail)
// 6、DescribeDocProcessJobs
DescribeJobsOpt := &cos.DescribeDocProcessJobsOptions{
QueueId: "p111a8dd208104ce3b11c78398f658ca8",
Tag: "DocProcess",
}
DescribeJobsRes, _, err := c.CI.DescribeDocProcessJobs(context.Background(), DescribeJobsOpt)
log_status(err)
fmt.Printf("%+v\n", DescribeJobsRes)
// 7、doc-preview
opt := &cos.DocPreviewOptions{
Page: 1,
}
resp, err := c.CI.DocPreview(context.Background(), "form.pdf", opt)
log_status(err)
fd, _ := os.OpenFile("form.pdf", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
io.Copy(fd, resp.Body)
fd.Close()
}

71
example/object/ci_video_auditing_job.go

@ -1,71 +0,0 @@
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"time"
"github.com/tencentyun/cos-go-sdk-v5"
"github.com/tencentyun/cos-go-sdk-v5/debug"
)
func log_status(err error) {
if err == nil {
return
}
if cos.IsNotFoundError(err) {
// WARN
fmt.Println("WARN: Resource is not existed")
} else if e, ok := cos.IsCOSError(err); ok {
fmt.Printf("ERROR: Code: %v\n", e.Code)
fmt.Printf("ERROR: Message: %v\n", e.Message)
fmt.Printf("ERROR: Resource: %v\n", e.Resource)
fmt.Printf("ERROR: RequestId: %v\n", e.RequestID)
// ERROR
} else {
fmt.Printf("ERROR: %v\n", err)
// ERROR
}
}
func main() {
bu, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com")
cu, _ := url.Parse("https://test-1259654469.ci.ap-guangzhou.myqcloud.com")
b := &cos.BaseURL{BucketURL: bu, CIURL: cu}
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: os.Getenv("COS_SECRETID"),
SecretKey: os.Getenv("COS_SECRETKEY"),
Transport: &debug.DebugRequestTransport{
RequestHeader: true,
RequestBody: true,
ResponseHeader: true,
ResponseBody: true,
},
},
})
opt := &cos.PutVideoAuditingJobOptions{
InputObject: "demo.mp4",
Conf: &cos.VideoAuditingJobConf{
DetectType: "Porn,Terrorism,Politics,Ads",
Snapshot: &cos.PutVideoAuditingJobSnapshot{
Mode: "Interval",
Start: 0.5,
TimeInterval: 50.5,
Count: 100,
},
},
}
res, _, err := c.CI.PutVideoAuditingJob(context.Background(), opt)
log_status(err)
fmt.Printf("%+v\n", res)
time.Sleep(3 * time.Second)
res2, _, err := c.CI.GetVideoAuditingJob(context.Background(), res.JobsDetail.JobId)
log_status(err)
fmt.Printf("%+v\n", res2)
}

7
object.go

@ -825,7 +825,6 @@ func (s *ObjectService) checkUploadedParts(ctx context.Context, name, UploadID,
}
// MultiUpload/Upload 为高级upload接口,并发分块上传
// 注意该接口目前只供参考
//
// 当 partSize > 0 时,由调用者指定分块大小,否则由 SDK 自动切分,单位为MB
// 由调用者指定分块大小时,请确认分块数量不超过10000
@ -870,8 +869,9 @@ func (s *ObjectService) Upload(ctx context.Context, name string, filepath string
return nil, rsp, err
}
result := &CompleteMultipartUploadResult{
Key: name,
ETag: rsp.Header.Get("ETag"),
Location: fmt.Sprintf("%s/%s", s.client.BaseURL.BucketURL, name),
Key: name,
ETag: rsp.Header.Get("ETag"),
}
if rsp != nil && s.client.Conf.EnableCRC {
scoscrc := rsp.Header.Get("x-cos-hash-crc64ecma")
@ -1001,7 +1001,6 @@ func (s *ObjectService) Upload(ctx context.Context, name string, filepath string
v, resp, err := s.CompleteMultipartUpload(context.Background(), name, uploadID, optcom)
if err != nil {
s.AbortMultipartUpload(ctx, name, uploadID)
return v, resp, err
}

1
object_part.go

@ -523,6 +523,7 @@ func (s *ObjectService) MultiCopy(ctx context.Context, name string, sourceURL st
v, resp, err := s.CompleteMultipartUpload(ctx, name, uploadID, optcom)
if err != nil {
s.AbortMultipartUpload(ctx, name, uploadID)
return nil, resp, err
}
cpres := &ObjectCopyResult{
ETag: v.ETag,

Loading…
Cancel
Save