Go用archive/zip压缩文件打不开,主因是header.Name未转义路径分隔符(需将\替换为/)、未调用zipWriter.Close()致EOCD缺失;解压时须用filepath.Clean校验路径防穿越;大文件需流式处理避免内存溢出。
archive/zip 压缩文件时,为什么生成的 zip 打不开?常见原

zip.FileHeader.Name,或忽略了路径分隔符转换。Windows 下路径是 \,而 ZIP 规范强制要求使用 /。直接写入本地路径会导致解压失败或目录结构错乱。
\ 替换为 /,再赋给 header.Name
filepath.WalkDir),对每个文件调用 zipWriter.CreateHeader
zipWriter.Close() —— 否则 zip 文件末尾缺少 EOCD(End of Central Directory)记录,绝大多数解压工具会报“损坏”fh, _ := zip.FileInfoHeader(fi) fh.Name = strings.ReplaceAll(filePath, "\\", "/") // 关键 fh.Name = strings.TrimPrefix(fh.Name, basePath+"/") w, _ := zw.CreateHeader(fh) io.Copy(w, f)
用户提供的 zip 包可能包含恶意路径如 ../../../etc/passwd,直接用 header.Name 创建文件会覆盖系统关键位置。必须做白名单校验。
filepath.Clean(header.Name) 归一化路径strings.HasPrefix + filepath.ToSlash 统一分隔符)".." 或以 "/" 开头的 Name
cleanName := filepath.Clean(header.Name)
if strings.Contains(cleanName, "..") || strings.HasPrefix(cleanName, "/") {
return fmt.Errorf("illegal path: %s", header.Name)
}
dstPath := filepath.Join(destDir, cleanName)
if !strings.HasPrefix(filepath.ToSlash(dstPath), filepath.ToSlash(destDir)+"/") {
return fmt.Errorf("escaping from target dir: %s", header.Name)
}
archive/tar 还是第三方库?Go 标准库已完整支持 archive/tar + compress/gzip 组合,无需引入外部依赖。但要注意:tar 本身不压缩,gzip 是外层封装,解压时要先 gzip.NewReader,再传给 tar.NewReader。
archive/zip 是单库全功能;archive/tar 必须搭配 compress/* 子包使用别把整个文件读进 []byte 再写入 zip —— 这会让内存占用飙升到文件大小的 2–3 倍。所有操作必须基于 io.Reader / io.Writer 流式完成。
os.Open 获取 *os.File,直接 io.Copy(zipWriter, file)
zip.File.Open() 拿到 io.ReadCloser,同样流式写入目标文件bufio.Writer 提升写入效率,但注意别在 zip 内部嵌套缓冲(标准库已优化)真正容易被忽略的是错误传播:流式操作中任一环节出错(如磁盘满、权限不足),必须立刻中断并清理已创建的临时文件,否则留下残缺 zip 或空目录。