tar.CreateHeader 报错因 Name 含非法字符、超长或为绝对路径;写入 tar.gz 必须按 tw.Close()→gw.Flush()→gw.Close() 顺序,否则解压失败。

tar.CreateHeader 为什么总报 archive/tar: cannot encode header
因为 tar.Header 的 Name 字段不能含非法字符(如 \0、控制符)、长度超 100 字节,且路径不能以 / 开头(否则会被视为绝对路径,Go tar 拒绝写入)。常见于直接传入 filepath.Abs() 结果或未清理用户输入的文件名。
- 用
filepath.Base()或strings.TrimPrefix(path, root)确保Name是相对路径且干净 -
Size必须设为真实字节长度(对目录可设为 0,但需手动设置Typeflag为tar.TypeDir) - 别依赖
tar.FileInfoHeader()处理 symlink 或 device 文件——它可能 panic,先用os.Stat()判断类型再构造
打包目录时如何递归遍历又避免符号链接循环
Go 标准库不自带安全遍历,filepath.Walk 默认会跟随 symlink,容易陷入循环或越界。必须显式控制。
- 用
filepath.WalkDir(Go 1.16+)替代filepath.Walk,它返回fs.DirEntry,可提前判断是否为 symlink - 遇到 symlink 时调用
os.Readlink(),再用filepath.IsAbs()和白名单路径比对,决定是否跳过 - 维护一个已访问的 inode+dev 号集合(用
sys.Stat_t.Ino+sys.Stat_t.Dev),在os.Lstat()后检查,防止硬链接循环
写入 tar.gz 时 gzip.Writer 要不要 Flush
要。漏掉 gzip.Writer.Flush() 会导致尾部 gzip 元数据丢失,解压时提示 gzip: invalid header 或只解出部分文件。
- 顺序必须是:
tw.Close()→gw.Flush()→gw.Close() -
tw.Close()只结束 tar 流,不触碰底层gzip.Writer;gw.Close()会 flush 并关闭,但若之前有写入错误,Close()才会暴露,所以建议显式Flush()后再Close() - 如果用
io.MultiWriter(gw, other),更得小心——Flush()不会透传,必须单独调用
从 tar 包读取文件内容时为何 tr.Next() 返回 nil 但没报错
说明 tar 流已到末尾,不是错误。但容易误以为“没读到文件”,其实是没调用 tr.Next() 就直接读了,或者上一次调用后没检查返回值就继续。
立即学习“go语言免费学习笔记(深入)”;
- 每次读文件前必须先调用
tr.Next(),它返回*tar.Header或io.EOF;nil表示 EOF,不是失败 - 别用
for tr.Next() != nil,正确写法是for { hdr, err := tr.Next(); if err == io.EOF { break }; if err != nil { ... } } - 如果 tar 包末尾有损坏块,
tr.Next()可能返回archive/tar: invalid tar header,此时应跳过而非终止










