diff --git a/config/config.yaml b/config/config.yaml index 90e8a6913..a7c1abc8d 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -73,11 +73,11 @@ object: secretAccessKey: openIM123 #秘钥 isDistributedMod: false #是否分布式多硬盘部署,如果是多硬盘部署,需要修改为true tencent: #tencent cos - appID: - region: - bucket: - secretID: - secretKey: + appID: 1314570205 + region: ap-guangzhou + bucket: openim-test-1314570205 + secretID: AKIDypY3K8lJk3NYVHqBXkneH5pMhsQFtKWX + secretKey: MZdXKxb9WhedR6dzAAzZyEVfgeR1NVP6 ali: #ali oss regionID: accessKeyID: diff --git a/go.mod b/go.mod index 0a8c1fd52..996933880 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/minio/minio-go/v7 v7.0.22 - github.com/mitchellh/mapstructure v1.4.2 + github.com/mitchellh/mapstructure v1.5.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.13.0 @@ -43,6 +43,8 @@ require ( github.com/go-sql-driver/mysql v1.6.0 github.com/go-zookeeper/zk v1.0.3 github.com/redis/go-redis/v9 v9.0.5 + github.com/tencentyun/cos-go-sdk-v5 v0.7.41 + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ) require ( @@ -57,6 +59,7 @@ require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/clbanning/mxj v1.8.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -70,6 +73,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.1 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect @@ -93,6 +97,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mozillazg/go-httpheader v0.4.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.18.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect diff --git a/go.sum b/go.sum index 443d6cb9d..bd13838a0 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OpenIMSDK/open_utils v1.0.8 h1:IopxWgJwEF5ZAPsRuiZZOfcxNOQOCt/p8VDENcHN9r4= github.com/OpenIMSDK/open_utils v1.0.8/go.mod h1:FLoaQblWUVKQgqt2LrNzfSZLT6D3DICBn1kcOMDLUOI= +github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/Shopify/sarama v1.29.0 h1:ARid8o8oieau9XrHI55f/L3EoRAhm9px6sonbD7yuUE= github.com/Shopify/sarama v1.29.0/go.mod h1:2QpgD79wpdAESqNQMxNc0KYMkycd4slxGdV3TWSVqrU= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= @@ -83,6 +84,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -202,6 +205,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -216,6 +222,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -314,8 +321,9 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -324,6 +332,9 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= +github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= +github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -412,6 +423,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4= +github.com/tencentyun/cos-go-sdk-v5 v0.7.41 h1:iU0Li/Np78H4SBna0ECQoF3mpgi6ImLXU+doGzPFXGc= +github.com/tencentyun/cos-go-sdk-v5 v0.7.41/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw= github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20210325043845-84a0811633ca h1:G/aIr3WiUesWHL2YGYgEqjM5tCAJ43Ml+0C18wDkWWs= github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20210325043845-84a0811633ca/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -784,6 +799,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 33d9b0a82..fbaa893e5 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -25,7 +25,14 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if err != nil { return err } - o, err := obj.NewMinioInterface() + // 根据配置文件策略选择 oss 方式 + enable := config.Config.Object.Enable + var o obj.Interface + if enable == "minio" { + o, err = obj.NewMinioInterface() + } else if enable == "tencent" { + o, err = obj.NewCosClient() + } if err != nil { return err } diff --git a/pkg/common/db/obj/obj.go b/pkg/common/db/obj/obj.go index e32c2479b..f10a42a1a 100644 --- a/pkg/common/db/obj/obj.go +++ b/pkg/common/db/obj/obj.go @@ -67,9 +67,9 @@ type Interface interface { TempBucket() string // DataBucket 永久存储的桶名 DataBucket() string - // PresignedGetURL 通过桶名和对象名返回URL + // PresignedGetURL 预签名授权 通过桶名和对象名返回URL PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error) - // PresignedPutURL 申请上传,返回PUT的上传地址 + // PresignedPutURL 预签名授权 申请上传,返回PUT的上传地址 PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) // GetObjectInfo 获取对象信息 GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error) diff --git a/pkg/common/db/obj/tx_oss.go b/pkg/common/db/obj/tx_oss.go new file mode 100644 index 000000000..d80fea05e --- /dev/null +++ b/pkg/common/db/obj/tx_oss.go @@ -0,0 +1,212 @@ +package obj + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/minio/minio-go/v7/pkg/s3utils" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/tencentyun/cos-go-sdk-v5" +) + +var conf = config.Config.Object.Tencent + +func create_url(bucket, region, source string) string { + return fmt.Sprintf("https://%s.cos.%s.myqcloud.com/%s", bucket, region, source) +} + +func NewCosClient() (Interface, error) { + u, err := url.Parse(create_url(conf.Bucket, conf.Region, "")) + if err != nil { + return nil, fmt.Errorf("tencent cos url parse %w", err) + } + b := &cos.BaseURL{BucketURL: u} + c := cos.NewClient(b, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: conf.SecretID, // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://cloud.tencent.com/document/product/598/37140 + SecretKey: conf.SecretKey, // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://cloud.tencent.com/document/product/598/37140 + }, + }) + return &cosImpl{ + client: c, + tempBucket: conf.Bucket, + }, err +} + +type cosImpl struct { + tempBucket string // 上传桶 + //dataBucket string // 永久桶 + urlstr string // 访问地址 + client *cos.Client +} + +func (c *cosImpl) Name() string { + return "tx_oss cos" +} + +func (c *cosImpl) MinFragmentSize() int64 { + return 1024 * 1024 * 5 // 每个分片最小大小 tx_oss.absMinPartSize +} + +func (c *cosImpl) MaxFragmentNum() int { + return 1000 // 最大分片数量 tx_oss.maxPartsCount +} + +func (c *cosImpl) MinExpirationTime() time.Duration { + return time.Hour * 24 +} + +func (c *cosImpl) TempBucket() string { + return c.tempBucket +} + +func (c *cosImpl) DataBucket() string { + //return c.dataBucket + return "" +} + +func (c *cosImpl) PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error) { + // 参考文档:https://cloud.tencent.com/document/product/436/14116 + // 获取对象访问 URL,用于匿名下载和分发 + presignedGetURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, conf.SecretID, conf.SecretKey, time.Hour, nil) + if err != nil { + return "", err + } + return presignedGetURL.String(), nil +} + +func (c *cosImpl) PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) { + // 参考文档:https://cloud.tencent.com/document/product/436/14114 + + if args.Effective <= 0 { + return "", errors.New("EffectiveTime <= 0") + } + _, err := c.GetObjectInfo(ctx, &BucketObject{ + Bucket: c.tempBucket, + Name: args.Name, + }) + if err == nil { + return "", fmt.Errorf("minio bucket %s name %s already exists", args.Bucket, args.Name) + } else if !c.IsNotFound(err) { + return "", err + } + // 获取预签名 URL + presignedPutURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodPut, args.Name, conf.SecretID, conf.SecretKey, time.Hour, nil) + if err != nil { + return "", fmt.Errorf("minio apply error: %w", err) + } + return presignedPutURL.String(), nil +} + +func (c *cosImpl) GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error) { + // https://cloud.tencent.com/document/product/436/7745 + // 新增参数 id 代表指定版本,如果不指定,默认查询对象最新版本 + head, err := c.client.Object.Head(ctx, args.Name, nil, "") + if err != nil { + return nil, err + } + size, _ := strconv.ParseInt(head.Header.Get("Content-Length"), 10, 64) + return &ObjectInfo{ + Size: size, + Hash: head.Header.Get("ETag"), + }, nil +} + +func (c *cosImpl) CopyObject(ctx context.Context, src *BucketObject, dst *BucketObject) error { + srcURL := create_url(src.Bucket, conf.Region, src.Name) + _, _, err := c.client.Object.Copy(ctx, dst.Name, srcURL, nil) + if err == nil { + _, err = c.client.Object.Delete(ctx, srcURL, nil) + if err != nil { + // Error + } + } + return err +} + +func (c *cosImpl) DeleteObject(ctx context.Context, info *BucketObject) error { + _, err := c.client.Object.Delete(ctx, info.Name) + return err +} + +func (c *cosImpl) ComposeObject(ctx context.Context, src []BucketObject, dst *BucketObject) error { + //TODO implement me + panic("implement me") +} + +func (c *cosImpl) IsNotFound(err error) bool { + ok, err := c.client.Object.IsExist(context.Background(), c.tempBucket) + if err == nil && ok { + fmt.Printf("object exists\n") + return true + } else if err != nil { + fmt.Printf("head object failed: %v\n", err) + return false + } else { + fmt.Printf("object does not exist\n") + return false + } +} + +func (c *cosImpl) CheckName(name string) error { + return s3utils.CheckValidObjectName(name) +} + +func (c *cosImpl) PutObject(ctx context.Context, info *BucketObject, reader io.Reader, size int64) (*ObjectInfo, error) { + /*// 采用高级接口, 无需用户指定 size + update, _, err := c.client.Object.Upload( + ctx, info.Name, info.Bucket, nil, + ) + if err != nil { + return nil, err + } + return &ObjectInfo{ + Hash: update.ETag, + }, nil*/ + // Case1 使用 Put 上传对象 + opt := &cos.ObjectPutOptions{ + ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{ + ContentType: "text/html", + }, + ACLHeaderOptions: &cos.ACLHeaderOptions{ + // 如果不是必要操作,建议上传文件时不要给单个文件设置权限,避免达到限制。若不设置默认继承桶的权限。 + XCosACL: "private", + }, + } + resp, err := c.client.Object.Put(ctx, info.Name, reader, opt) + if err != nil { + return nil, err + } + return &ObjectInfo{ + Hash: resp.Header.Get("ETag"), + }, nil +} + +func (c *cosImpl) GetObject(ctx context.Context, info *BucketObject) (SizeReader, error) { + opt := &cos.MultiDownloadOptions{ + ThreadPoolSize: 5, + } + update, err := c.client.Object.Download( + ctx, info.Name, info.Bucket, opt, + ) + if err != nil { + return nil, err + } + size, _ := strconv.ParseInt(update.Header.Get("Content-Length"), 10, 64) + body := update.Body + if body == nil { + return nil, errors.New("response body is nil") + } + readCloser, ok := body.(io.ReadCloser) + if !ok { + return nil, errors.New("failed to convert response to ReadCloser") + } + return NewSizeReader(readCloser, size), nil +}