能用 aws-sdk-go-v2/service/s3 对接腾讯云 COS,只需正确配置 endpoint、region、credentials 和 signingName 为 "cos",并禁用 path-style;官方 COS SDK 耦合重、更新慢、协议兼容性差,而 AWS SDK 生态成熟、调试方便、可复用工具链。

直接用 aws-sdk-go-v2/service/s3 就能对接腾讯云 COS,不用官方 COS SDK —— 只要 endpoint、region、credentials 和 signingName 配对正确,签名就能过;但默认配置下会报 InvalidSignatureException 或 NotFound: The specified bucket does not exist,问题几乎都出在 region 和 endpoint 的映射关系上。
为什么不用腾讯云官方 COS SDK?
官方 github.com/tencentyun/cos-go-sdk-v5 功能完整,但耦合重、更新慢、日志埋点不透明,且和 AWS S3 协议不完全对齐(比如 ListObjectsV2 的 delimiter 行为、ETag 计算方式)。而 aws-sdk-go-v2/service/s3 是事实标准,生态成熟、调试工具多(如 aws-cli 可复用)、支持 ResolveUnknownService,只要绕过 region 自动解析就能通用。
- 腾讯云 COS 的 endpoint 格式是
https://<bucket-name>-<appid>.cos.<region>.myqcloud.com</region></appid></bucket-name>,不是cos.<region>.myqcloud.com</region> -
region必须设为真实地域缩写(如ap-beijing),不能填cn-bj或空字符串 - 必须显式设置
signingName: "cos",否则 SDK 用默认的"s3"签名,COS 拒绝 - 启用
ResolveUnknownService: true,否则 SDK 会因不认识cos服务名而 panic
如何配置 aws-sdk-go-v2 连接 COS
关键不是换 SDK,而是把 COS 当成“兼容 S3 的第三方实现”来用。以下是最小可用配置:
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("ap-beijing"),
config.WithEndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if service == "s3" {
return aws.Endpoint{URL: "https://my-bucket-1250000000.cos.ap-beijing.myqcloud.com"}, nil
}
return aws.Endpoint{}, fmt.Errorf("unknown service %s", service)
}),
),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
"AKIDxxx",
"SECKEYxxx",
"",
)),
config.WithRetryer(func() awsmiddleware.Retryer {
return retry.AddWithErrorCodes(retry.NewStandard(), "RequestExpired")
}),
config.WithAPIOptions(
// 关键:覆盖 signing name
[]func(*middleware.Stack) error{
func(stack *middleware.Stack) error {
return stack.Finalize.Add(middleware.AddUserAgentKeyValue("cos-compat", "1.0"), middleware.Before)
},
}...,
),
)
// 再手动注入 signingName
cfg.Credentials = credentials.NewCredentialsCache(cfg.Credentials)
cfg.Region = "ap-beijing"
s3Client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.UsePathStyle = false // COS 不支持 path-style,必须用 virtual-hosted-style
o.SigningName = "cos" // 强制设为 cos,不是 s3
})
-
UsePathStyle = false:COS 要求 bucket 名在域名中,不能放在 path 里 -
SigningName = "cos":必须显式覆盖,否则签名头是Authorization: AWS4-HMAC-SHA256 ...但 COS 验签时期望COS4-HMAC-SHA256 - endpoint 中的
my-bucket-1250000000必须和实际 bucket 名+APPID 一致,否则 404 - 不要依赖
config.WithEndpointResolver自动补 region,COS 的 endpoint 不符合 AWS 命名惯例
Gin 中封装上传逻辑的注意事项
别在每个 handler 里重复初始化 client,也别把 credentials 硬编码进 handler。推荐按 GoFly 框架思路做统一 upload provider:
立即学习“go语言免费学习笔记(深入)”;
- 在
init()或 app startup 阶段,根据配置项(如storage.type == "tencent-cos")构建并缓存一个全局*s3.Client - 上传时用
s3.PutObjectInput,注意Key字段不能以/开头,否则 COS 返回 403 - COS 不校验
Content-MD5,但会计算并返回ETag—— 它是 base64(MD5) 而非 hex,和 AWS 不同,别拿它做一致性校验 - 分块上传(
CreateMultipartUpload)时,PartNumber必须从 1 开始连续,跳号或重复会导致InvalidPart - 删除文件用
DeleteObject,路径是相对 key(如uploads/abc.jpg),不是带域名的 URL
最常被忽略的是:COS 的 region 和 endpoint 必须严格匹配,且 signingName 不能靠 guess —— 错一个字符,错误提示就只有 InvalidSignatureException,没别的线索。


















