Golang tar与zip标准库怎么用_Golang归档文件操作

10次阅读

用 archive/tar 打包目录时权限丢失,因手动构造 tar.Header 未设 Mode/Uid/Gid;正确做法是用 tar.FileInfoHeader(fi, “”) 生成后再改 Name 为相对路径,并为目录设 TypeDir 和末尾 /。

Golang tar与zip标准库怎么用_Golang归档文件操作

archive/tar 打包目录时,为什么解压后权限全丢了?

因为 tar.FileInfoHeader 虽能自动填充 ModeUidGid 等字段,但如果你手动构造 tar.Header 并只设了 NameSize,就等于放弃了所有元数据。解压工具看到空权限,只能按默认值(如 0644)创建文件。

  • ✅ 正确做法:始终用 tar.FileInfoHeader(fi, "") 生成 header,再手动覆盖 hdr.Name 为相对路径(如 "src/main.go"
  • ⚠️ 注意:fi.Name() 是文件名,不是完整路径;不修正 hdr.Name 会导致所有文件都写到 tar 根目录下
  • ? 目录条目必须设 hdr.Typeflag = tar.TypeDir,且 hdr.Name 末尾带 /,否则解压器可能忽略它
  • ? 权限位(如 0755)会原样保留,但 windows 上无意义;linux/macOS 解压时可直接生效

archive/zip 压缩中文文件名,为什么解压乱码?

ZIP 规范允许 UTF-8 编码文件名,但默认关闭。Go 的 zip.FileHeader 不自动启用它,多数解压工具(尤其是 Windows 自带的)仍按 CP437 解码,结果就是一 或乱码。

  • ✅ 必须显式设置:header.Flags = 1(即 zip.UseUTF8 标志),否则中文名无效
  • ⚠️ 路径分隔符必须用 /,哪怕在 Windows 上调用 filepath.ToSlash() —— 用 会导致 7-Zip 等工具无法识别子目录
  • ? 打包目录时,filepath.Walk 会钻进 .gitnode_modules,得手动过滤:if strings.HasPrefix(fi.Name(), ".") || fi.Mode()&os.ModeSymlink != 0
  • ? 小文件可用 w.Create("a.txt") 返回的 io.Writer;大文件务必用 w.CreateHeader(header) + io.copy,避免内存暴涨

tar + gzip 合成 .tar.gz,为什么解压报 invalid checksum

因为 gzip 是流式压缩,尾部有校验数据;如果没等 tar.Writer 写完就关了 gzip.Writer,这部分就丢了。错误顺序或漏掉某层 Close() 都会触发这个错。

  • ✅ 正确链路:file → gzip.NewWriter(file) → tar.NewWriter(gzWriter)
  • ✅ 关闭顺序必须严格倒序:tarWriter.Close()gzipWriter.Close()file.Close()
  • ⚠️ 千万别省略 tarWriter.Close():它负责写入 tar 结束块(两个 512 字节零块),缺了会导致部分解压器卡住或截断
  • ⚡ 想更高压缩率?把 gzip.NewWriter 换成 zstd.NewWriter(需引入 github.com/klauspost/compress/zstd),API 完全一致

解压 ZIP 或 TAR 时,如何防止 ../../etc/passwd 这类路径遍历攻击?

用户提供的 ZIP/TAR 文件不可信。直接拼接 destDir + header.Name 可能跳出目标目录,覆盖系统关键文件。

立即学习go语言免费学习笔记(深入)”;

  • ✅ 解压前必须清洗路径:cleanPath := filepath.Clean(header.Name)
  • ❌ 拒绝两种情况:cleanPath != header.Name(含 .. 或多余 /),或 strings.HasPrefix(cleanPath, "..")
  • ? 创建目录前,先 os.MkdirAll(filepath.Dir(dstPath), 0755);写文件前确认 !fi.IsDir()
  • ? zip.Filetar.Header 都不保证路径安全 —— 这是调用方的责任,标准库不代劳

最易被忽略的点:所有归档操作都依赖准确的 Size 字段(tar.Header.Size / zip.FileHeader.UncompressedSize64)。设错会导致解压时内容截断或阻塞,而 Go 标准库不会校验它是否匹配实际读取字节数 —— 你得自己确保 io.Copy 写入量和 header 里填的一致。

text=ZqhQzanResources