aws-sdk-go-v2 是官方推荐的 s3 客户端,相比 v1 更安全可控:显式传 config 避免并发配置污染,需调用 config.loaddefaultconfig() 并手动覆盖 endpoint;oss 要设 region 为真实值且签名强制 v4,cos 需启用 usepathstyle;上传前须设 contenttype(可用 mime.typebyextension 推断),windows 路径需先 toslash;gzip 上传要同时设 contenttype 和 contentencoding;deleteobjects 单次上限 1000 个 key,超限会报 malformedxml。

用 aws-sdk-go-v2 连 S3,别碰 aws-sdk-go v1
新版 SDK 是云厂商官方推荐路径,v1 已停止功能更新,且 v2 的 context 传递、重试策略、凭证链默认行为更可控。v1 里 session.Must() 和隐式全局配置容易在并发备份任务中串配置;v2 显式传 config.Config,每个备份任务可隔离 region、endpoint、credentials。
- 初始化时必须显式调用
config.LoadDefaultConfig(),不能只靠环境变量——某些云厂商(如腾讯 COS)的 endpoint 需手动覆盖EndpointResolverWithOptions - 阿里 OSS 兼容 S3 协议,但签名版本强制
v4,且需把region设为oss-cn-hangzhou这类真实值,设成us-east-1会报InvalidRequest: The authorization header is malformed - 腾讯 COS 要额外设置
UsePathStyle为true,否则生成的 URL 域名是bucket.cos.ap-guangzhou.myqcloud.com(错误),正确应为cos.ap-guangzhou.myqcloud.com/bucket
上传文件前先检查 PutObjectInput.ContentType 是否为空
不设这个字段,S3 默认存为 binary/octet-stream,后续通过 URL 直接访问时浏览器可能触发下载而非渲染;OSS 更严格:若传空字符串,部分 SDK 会直接 panic 或返回 InvalidArgument: The Content-Type you specified is not valid。
- 用
mime.TypeByExtension(filepath.Ext(path))自动推断,fallback 到application/octet-stream - 注意 Windows 路径分隔符:
filepath.Ext("C:\data\report.pdf")返回空,得先filepath.ToSlash() - gzip 压缩后上传要显式设
ContentType为源类型(如text/html),再加ContentEncoding: "gzip",否则 CDN 或浏览器无法自动解压
批量删除旧备份时,DeleteObjects 最多一次删 1000 个 key
S3/OSS 接口限制硬性卡在 1000 条,超了会直接返回 MalformedXML 错误,不是分页提示。自动化脚本如果按时间戳扫出 5000 个待删文件,不切片就发请求,整个任务就挂。
- 用
for i := 0; i 手动分块,每块构造独立的 <code>DeleteObjectsInput - 别依赖
ListObjectsV2的MaxKeys当删除上限——它只控制列出数量,和删除接口无关 - 阿里 OSS 的
DeleteObjects不支持 quiet 模式,返回体必带成功/失败列表,务必检查Errors字段,有失败项得单独重试,不能只看 HTTP 状态码 200
备份失败时,err 里藏了云厂商真实错误码,别只打 err.Error()
比如 S3 返回 NotFound,OSS 返回 NoSuchBucket,COS 返回 NoSuchBucket 但 HTTP 状态码是 404 —— 表面一样,实际含义不同:前者是桶不存在,后者可能是权限不足被伪装成 404。只输出字符串,排查时全靠猜。
立即学习“go语言免费学习笔记(深入)”;
- 用
awshttp.ErrStatusCode(err)(v2 SDK)或resp.ResultMetadata.StatusCode拿状态码 - 对 AWS:用
awss3.IsNotFound(err)判断;对 OSS:正则匹配"NoSuchBucket|NoSuchKey";对 COS:检查err.(awshttp.RequestFailure).Code()是否含"NoSuchBucket" - 日志里同时记下
reqID := resp.ResultMetadata.RequestID,这是云厂商查问题的唯一依据,丢了就只能等下次复现
最麻烦的是跨厂商抽象层——看着用一套 interface 封装了 S3/OSS/COS,结果每个 delete 失败的重试逻辑、每个 head 请求的 404 判定都得单独适配。没在真实环境跑过三种云的备份恢复流程,就别急着写“通用对象存储客户端”。










