tar.newreader接收gzip.reader会错乱,因gzip.newreader预读至少10字节破坏tar header起始位置;必须用未被预读的原始io.reader(如*os.file或bytes.reader)创建gzip.reader,再将其直接传入tar.newreader。

gzip.NewReader 不能直接传给 tar.NewReader —— 解压会错乱,文件名、权限全丢。
为什么 tar.NewReader 拿到 gzip.Reader 就出错?
因为 gzip.NewReader 内部会预读至少 10 字节来识别 gzip header,而 tar.NewReader 要求输入流从真正的 tar header 起始位置开始读。预读导致偏移错位,后续所有 tar.Header 解析都偏移失效。
- 不是“gzip 不支持 tar”,是流位置被破坏了
- 哪怕源是
*os.File,只要在gzip.NewReader前调过Peek或Read,就不可逆地丢失起始点 - HTTP body 这类一次性流更危险:一旦预读失败,整个 stream 就废了
流式解压 tar.gz 的正确链路
必须让 tar.NewReader 看到未被干扰的原始压缩流起点。关键不是“绕过 gzip”,而是“控制预读时机”。
- 用
gzip.NewReader包装原始io.Reader(如*os.File或bytes.Reader),它返回一个*gzip.Reader - 把这个
*gzip.Reader直接传给tar.NewReader—— 别取它的字段,别二次包装 - 确保原始 reader 可重放:文件句柄 OK,
bytes.ReaderOK,net/http.Response.Body必须先用io.TeeReader或bytes.Buffer缓存前几个字节做 magic check - 不要手动调
tr.Next()后再读数据;每次tr.Next()都隐式跳过 padding,后续io.CopyN才能对齐
tar.Header.Name 的路径遍历风险怎么拦?
tar.Header.Name 是用户可控输入,filepath.Clean 不足以防御绕过 —— 比如 "..oo" 在 Windows 上可能逃逸校验。
- 先用
filepath.FromSlash(header.Name)统一转成本地路径分隔符 - 再用
filepath.Clean归一化,得到cleaned - 检查
cleaned是否以目标根目录(如"tmp")开头,且!strings.Contains(cleaned, "..") - 拒绝空名、以
/开头、含

