go解压tar.gz需先用gzip.newreader解压再传给tar.newreader;须用filepath.clean校验路径防遍历攻击;勿信header.size,优先io.copy;windows中文名乱码需上游改pax格式或换archiver库。

Go怎么用archive/tar和compress/gzip解压tar.gz
Go标准库不提供“一键解压tar.gz”的函数,必须手动组合gzip.Reader和tar.Reader——这是最常被卡住的第一步。
典型错误是直接对.tar.gz文件调用tar.NewReader,结果读到的全是乱码或立即报EOF。因为tar.Reader只认裸tar流,不处理gzip压缩层。
- 先用
os.Open打开文件,再套一层gzip.NewReader - 把
gzip.Reader传给tar.NewReader,才能正确解析 - 注意:
gzip.NewReader会自动检测gzip头,但不会校验尾部CRC——出错时往往在解包中途panic,而不是开头就失败
file, _ := os.Open("data.tar.gz")
defer file.Close()
gzr, _ := gzip.NewReader(file)
defer gzr.Close()
tr := tar.NewReader(gzr)
解压时如何安全处理路径遍历(../攻击)
Go的tar.Reader默认不做路径过滤,恶意tar包里放../../../etc/passwd会直接写到系统任意位置——这是生产环境最危险的坑。
不能只靠strings.HasPrefix(header.Name, "../")简单判断,因为../../foo、/etc/shadow、.\..\passwd都可能绕过。
- 用
filepath.Clean(header.Name)标准化路径,再检查是否以目标目录为前缀 - 目标目录末尾必须加
filepath.Separator(如/tmp/),否则/tmp2/xxx会被误判为合法 - 跳过
header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeLink的条目,避免符号链接逃逸
tar.Header.Size不准导致解压中断或卡死
某些打包工具生成的tar包里Size字段是0或负数,用io.CopyN(dst, tr, header.Size)会直接panic或无限读取。
别无条件信任header.Size,尤其面对第三方来源的tar.gz。
- 优先用
io.Copy(dst, tr),让tar.Reader自己按块边界停 - 如果必须限长(比如防内存爆),用
io.LimitReader(tr, maxBytes)替代CopyN - 读完一个文件后,调用
tr.Next()跳到下一个Header,否则会卡在当前文件末尾
Windows下解压中文文件名乱码怎么办
Go标准库archive/tar按POSIX规范处理文件名,不识别GNU tar的PAX扩展或UTF-8标志位——所以Linux打包的含中文tar.gz,在Windows上解压后文件名变成.txt。
没有银弹,只能按来源选策略:
- 若确定源是Linux且用了UTF-8编码,用
golang.org/x/text/encoding/simplifiedchinese.GBK.NewDecoder().String(header.Name)硬转(不推荐) - 更稳妥的是要求上游改用PAX格式:
tar --format=pax -czf data.tar.gz files/ - 或者换用
github.com/mholt/archiver/v4,它自动探测并处理常见编码
真正麻烦的是混合编码:同一个tar包里有的文件名是GBK,有的又是UTF-8——这种只能人工介入,Go标准库无解。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











