Go的archive/tar仅打包不压缩,必须套gzip.Writer才能生成真正压缩的tar.gz;tar.Header.Name需UTF-8、Unix路径格式,Mode须显式设0644/0755,gzip.Writer.Close()不可省略,否则文件损坏。

归档前先确认 tar.gz 是否真能压缩目录
Go 的 archive/tar 本身不压缩,它只打包装,压缩得靠 gzip。直接用 tar.Writer 写出的文件是纯 tar(未压缩),体积大、上传慢、对象存储计费高。常见错误是以为 tar.Create 自带 gzip,结果传上去一堆几百 MB 的 .tar 文件,还纳闷为什么解压失败。
- 必须显式套一层
gzip.Writer,顺序不能反:先tar.NewWriter→ 再写入文件 → 最后gzip.Close()(触发实际压缩) - 注意
os.Chdir或路径拼接问题:如果用相对路径遍历目录,tar.Header.Name必须是 Unix 风格(/分隔)、无盘符、无前导./,否则某些对象存储(如 AWS S3)解压时会建错层级 - 别忽略
Header.Size和Header.Mode:size 要设为文件真实字节长度,mode 建议用0644(文件)或0755(可执行),否则部分 SDK 解压后权限异常
上传时绕过内存缓冲,用流式直传
大文件(>100MB)全读进 []byte 再传,容易 OOM。对象存储 SDK(如 aws-sdk-go 或 minio-go)都支持 io.Reader 接口,归档过程本身就能产出 Reader —— 关键是别提前 close。
- 用
io.Pipe()搭配 goroutine:一端写 tar+gzip 流,另一端交给PutObject;pipe 的 write side 必须在 goroutine 中调用Close(),否则上传卡住 - MinIO 示例中,
minio.PutObjectOptions.Progress可传进度回调,但注意它每 5MB 触发一次,别在里面做重 IO 操作 - AWS S3 的
PutObjectInput.Body接受任意io.ReadSeeker,但 tar+gzip 流不支持 seek,所以必须用PutObject(非Uploadmultipart),否则报ReadSeeker is not seekable
处理中文路径和特殊字符文件名
Go 默认用系统 locale 编码文件名,Linux/macOS 是 UTF-8,Windows 是 GBK,一上传就乱码。S3 和兼容 S3 的对象存储(如 MinIO、腾讯云 COS)要求 Header.Name 是 UTF-8 字节序列,且不能含 <p>Go 默认用系统 locale 编码文件名,Linux/macOS 是 UTF-8,Windows 是 GBK,一上传就乱码。S3 和兼容 S3 的对象存储(如 MinIO、腾讯云 COS)要求 <code>Header.Name 是 UTF-8 字节序列,且不能含