生成的 ZIP 文件打不开,主因是未正确设置 zip.FileHeader.Name:必须为非空、正斜杠分隔的相对路径,禁止以 / 开头或含 ../,且须调用 zipWriter.Close() 写入中央目录。

用 archive/zip 包压缩多个文件时,为什么生成的 zip 文件打不开?
常见原因是没正确设置 zip.FileHeader.Name,导致路径混乱或文件名为空。ZIP 标准要求文件名不能以 / 开头,也不能包含 .. 路径穿越,否则多数解压工具会拒绝打开。
- 对每个待压缩文件,用
filepath.Base()提取纯文件名,或用strings.TrimPrefix(filepath.ToSlash(filePath), baseDir)生成相对路径 - 必须显式调用
header.SetModTime(),否则部分系统(如 macOS)解压后文件时间戳异常,可能影响构建缓存判断 - 写入内容前需先调用
zipWriter.CreateHeader(header),不能直接Write()到zipWriter
file, _ := os.Open("input.txt")
defer file.Close()
header, _ := zip.FileInfoHeader(file.Stat())
header.Name = "input.txt" // 必须设为 ZIP 内部路径,非磁盘绝对路径
header.Method = zip.Deflate
writer, _ := zipWriter.CreateHeader(header)
io.Copy(writer, file)
解压 zip 文件时遇到 “invalid checksum” 或 “not a valid zip file” 错误
这类错误往往不是 ZIP 损坏,而是读取方式不对:直接传入未 Seek 的 *os.File 给 zip.NewReader(),而该函数内部会多次读取开头字节校验魔数 —— 如果文件指针已在中间位置,就会失败。
- 确保传给
zip.NewReader()的io.Reader是从开头开始的,例如用os.Open()后不调用Seek(),或用bytes.NewReader(data)处理内存数据 - 若从 HTTP 响应体解压,需先
io.ReadAll(resp.Body)再传给zip.NewReader();流式解压需额外处理边界,不推荐初学者尝试 - 检查是否误把 gzip 数据当 zip 解压 ——
gzip: invalid header和zip: not a valid zip file是不同错误,别混用包
需要同时支持 tar.gz 和 zip,但不想引入大量第三方依赖
Go 标准库已覆盖全部基础需求:archive/tar + compress/gzip 处理 .tar.gz,archive/zip 处理 .zip。二者 API 设计思路接近,但关键差异必须注意。
-
tar.Header.Name允许含路径(如src/main.go),而zip.FileHeader.Name同样允许,但需手动确保不含../防止路径逃逸 -
tar.Writer写完每个文件后必须调用tarWriter.WriteHeader()和io.Copy(),且最后要tarWriter.Close()补齐两个空块;zip.Writer则只需CreateHeader()+Write(),结尾Close()自动写目录结构 - gzip 层是包装器:先创建
gzip.NewWriter(out),再用它初始化tar.NewWriter();顺序反了会得到不可解压的二进制
压缩大文件时内存暴涨甚至 OOM
根本原因是把整个文件读进内存再写入 zip —— os.ReadFile() 或 io.ReadAll() 会分配等同于文件大小的字节切片。
立即学习“go语言免费学习笔记(深入)”;
- 始终使用流式处理:打开源文件后,用
io.Copy()直接从*os.File写入zip.Writer对应的io.Writer - 避免用
bytes.Buffer中转,它仍是内存缓冲;如需中间处理(如加密),改用io.Pipe()配合 goroutine 边读边转 - 对超大单文件(>1GB),考虑分块压缩逻辑或改用外部命令(
exec.Command("7z", "a", ...)),标准库 zip 不支持分卷
路径处理和流式读写是跨格式压缩解压里最容易被跳过的两步,漏掉任意一个,程序在小样本上跑得通,上线后遇到真实文件就出问题。


















