Go 语言标准库 —— archive/zip 包(zip 归档处理)
🔹 常量
存储方法(无压缩)
zip.Store
-
值:0,表示不压缩直接存储
-
示例
package main import ( "archive/zip" "fmt" ) func main() { // 创建 zip 文件 zipFile, _ := zip.Create("test.zip") defer zipFile.Close() // 使用 Store 方法(不压缩) writer := zip.NewWriter(zipFile) header := &zip.Header{ Name: "file.txt", Method: zip.Store, // 不压缩 } fmt.Printf("压缩方法:%d (0=Store, 8=Deflate)\n", header.Method) }
Deflate 压缩方法
zip.Deflate
-
值:8,使用 Deflate 算法压缩
-
示例
package main import ( "archive/zip" "fmt" ) func main() { header := &zip.Header{ Name: "compressed.txt", Method: zip.Deflate, // 使用 Deflate 压缩 } fmt.Printf("使用 Deflate 压缩:%d\n", header.Method) }
🔹 类型
zip.File
zip.File struct
-
说明:表示 zip 归档中的一个文件
-
字段:
- FileHeader - 文件头
- compressedMethod uint16 - 压缩方法
- compressedSize uint32 - 压缩后大小
- uncompressedSize uint32 - 压缩前大小
- Reader - 读取器
-
示例
package main import ( "archive/zip" "fmt" "io" "os" ) func main() { // 打开 zip 文件 r, err := zip.OpenReader("test.zip") if err != nil { fmt.Println("打开失败:", err) return } defer r.Close() // 遍历文件 for _, f := range r.File { fmt.Printf("文件:%s\n", f.Name) fmt.Printf(" 压缩方法:%d\n", f.Method) fmt.Printf(" 压缩后大小:%d\n", f.CompressedSize64) fmt.Printf(" 原始大小:%d\n", f.UncompressedSize64) // 读取内容 rc, err := f.Open() if err != nil { fmt.Println("打开失败:", err) continue } content, _ := io.ReadAll(rc) rc.Close() fmt.Printf(" 内容:%s\n", string(content)) } }
zip.FileHeader
zip.FileHeader struct
-
说明:zip 文件的文件头信息,包含文件的元数据
-
字段详解:
- Name string - 文件名或路径(支持相对路径和绝对路径)
- Method uint16 - 压缩方法(zip.Store=0 不压缩,zip.Deflate=8)
- Modified time.Time - 最后修改时间
- CRC32 uint32 - CRC32 校验值(用于验证数据完整性)
- CompressedSize64 uint64 - 压缩后的大小(字节)
- UncompressedSize64 uint64 - 压缩前的原始大小(字节)
- ExternalAttrs []byte - 外部文件属性(如 Unix 权限)
- InternalAttrs uint16 - 内部文件属性
- Comment string - 文件注释
- Extra []byte - 额外字段(用于扩展信息)
- CreatorVersion uint16 - 创建者版本
- ReaderVersion uint16 - 读取所需版本
- Flags uint16 - 标志位
-
注意事项:
- Name 字段应使用正斜杠(/)作为路径分隔符
- 对于目录,Name 通常以 / 结尾
- Method 必须是 zip.Store 或 zip.Deflate
- Modified 时间会被转换为 DOS 格式存储
- CRC32 在写入数据后自动计算
-
示例(完整)
package main import ( "archive/zip" "fmt" "time" ) func main() { header := &zip.FileHeader{ Name: "test.txt", Method: zip.Deflate, Modified: time.Now(), } fmt.Printf("文件名:%s\n", header.Name) fmt.Printf("压缩方法:%d\n", header.Method) fmt.Printf("修改时间:%v\n", header.Modified) } -
使用场景示例
-
创建普通文件头
- 示例:
header := &zip.FileHeader{ Name: "file.txt", Method: zip.Deflate, Modified: time.Now(), }
- 示例:
-
创建目录头
- 示例:
header := &zip.FileHeader{ Name: "mydir/", }
- 示例:
-
使用 FileInfoHeader 创建
- 示例:
info, _ := os.Stat("file.txt") header, _ := zip.FileInfoHeader(info, "") header.Method = zip.Deflate
- 示例:
-
设置自定义时间
- 示例:
header.Modified = time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
- 示例:
-
zip.Reader
zip.Reader struct
-
说明:用于读取 zip 归档
-
字段:
- File []*File - 文件列表(按顺序排列)
- Comment string - zip 归档的注释
-
常用方法详解
-
Open 方法
- 说明:打开 zip 中的文件进行读取
- 方法:
(f *File) Open() (io.ReadCloser, error) - 注意:返回的 ReadCloser 需要关闭
- 示例:
rc, err := file.Open() if err != nil { return err } defer rc.Close()
-
FileInfo 方法
- 说明:返回文件的 FileInfo 接口
- 方法:
(f *File) FileInfo() fs.FileInfo - 注意:用于判断是否为目录、获取权限等
- 示例:
info := file.FileInfo() if info.IsDir() { // 是目录 }
-
-
示例(完整)
package main import ( "archive/zip" "fmt" "io" "strings" ) func main() { // 从内存创建 zip 读取器 data := []byte("模拟 zip 数据...") reader := strings.NewReader(string(data)) // 创建 zip 读取器 r, err := zip.NewReader(reader, int64(len(data))) if err != nil { fmt.Println("创建失败:", err) return } fmt.Printf("文件数量:%d\n", len(r.File)) fmt.Printf("注释:%s\n", r.Comment) // 遍历文件 for _, f := range r.File { fmt.Printf("文件:%s\n", f.Name) // 读取内容 rc, _ := f.Open() content, _ := io.ReadAll(rc) rc.Close() fmt.Printf(" 内容:%s\n", string(content)) } } -
使用场景示例
-
查找特定文件
- 示例:
r, _ := zip.OpenReader("archive.zip") defer r.Close() for _, f := range r.File { if f.Name == "config.txt" { rc, _ := f.Open() defer rc.Close() data, _ := io.ReadAll(rc) fmt.Println(string(data)) } }
- 示例:
-
统计压缩率
- 示例:
var totalCompressed, totalUncompressed uint64 for _, f := range r.File { if !f.FileInfo().IsDir() { totalCompressed += f.CompressedSize64 totalUncompressed += f.UncompressedSize64 } } ratio := float64(totalCompressed) * 100 / float64(totalUncompressed) fmt.Printf("压缩率:%.1f%%\n", ratio)
- 示例:
-
列出所有文件
- 示例:
for _, f := range r.File { fileType := "文件" if f.FileInfo().IsDir() { fileType = "目录" } fmt.Printf("%s [%s] %d bytes\n", f.Name, fileType, f.UncompressedSize64) }
- 示例:
-
zip.ReadCloser
zip.ReadCloser struct
-
说明:可关闭的 zip 读取器,嵌入了 *zip.Reader
-
字段:
- 继承 zip.Reader 的所有字段(File、Comment)
- 内部包含关闭方法
-
常用方法详解
- Close 方法
- 说明:关闭 zip 文件和读取器
- 方法:
Close() error - 注意:
- 必须调用,释放文件句柄
- 应该在 defer 中调用确保关闭
- 示例:
rc, _ := zip.OpenReader("archive.zip") defer rc.Close() // 确保关闭
- Close 方法
-
示例(完整)
package main import ( "archive/zip" "fmt" ) func main() { // 打开 zip 文件 rc, err := zip.OpenReader("test.zip") if err != nil { fmt.Println("打开失败:", err) return } defer rc.Close() fmt.Printf("文件数量:%d\n", len(rc.File)) // 遍历文件 for _, f := range rc.File { fmt.Printf("文件:%s\n", f.Name) } } -
使用场景示例
-
批量读取文件
- 示例:
rc, _ := zip.OpenReader("archive.zip") defer rc.Close() for _, f := range rc.File { processFile(f) }
- 示例:
-
条件读取
- 示例:
rc, _ := zip.OpenReader("data.zip") defer rc.Close() for _, f := range rc.File { if strings.HasSuffix(f.Name, ".txt") { readTextFile(f) } }
- 示例:
-
zip.Writer
zip.Writer struct
-
说明:用于写入 zip 归档
-
常用方法详解
-
Create 方法
- 说明:在 zip 中创建一个新文件
- 方法:
Create(name string) (io.Writer, error) - 注意:
- 使用默认设置创建文件
- 自动处理路径分隔符(使用 /)
- 返回的 io.Writer 用于写入文件内容
- 示例:
fw, err := w.Create("file.txt") if err != nil { return err } fw.Write([]byte("content"))
-
CreateHeader 方法
- 说明:使用自定义 FileHeader 创建文件
- 方法:
CreateHeader(fh *FileHeader) (io.Writer, error) - 注意:
- 可以设置压缩方法、时间等属性
- 创建目录时 Name 以 / 结尾
- 更灵活,推荐使用
- 示例:
header := &zip.FileHeader{ Name: "file.txt", Method: zip.Deflate, } fw, _ := w.CreateHeader(header) fw.Write([]byte("content"))
-
CreateRaw 方法
- 说明:创建文件并直接写入原始数据
- 方法:
CreateRaw(fh *FileHeader) (io.Writer, error) - 注意:
- 用于写入已经压缩的数据
- 需要手动设置 CRC32 和大小
- 示例:
header := &zip.FileHeader{ Name: "compressed.txt", Method: zip.Deflate, CompressedSize64: size, UncompressedSize64: usize, CRC32: crc, } fw, _ := w.CreateRaw(header) fw.Write(compressedData)
-
SetComment 方法
- 说明:设置 zip 归档的注释
- 方法:
SetComment(comment string) error - 注意:
- 注释长度有限制(通常 64KB)
- 必须在 Close() 之前调用
- 示例:
w.SetComment("这是备份文件")
-
RegisterCompressor 方法
- 说明:注册自定义压缩器
- 方法:
RegisterCompressor(method uint16, comp Compressor) - 注意:
- 用于支持非标准压缩方法
- 通常不需要使用
- 示例:
// 注册自定义压缩器 zip.RegisterCompressor(zip.Deflate, func(w io.Writer) (io.WriteCloser, error) { return flate.NewWriter(w, flate.DefaultCompression) })
-
Close 方法
- 说明:完成 zip 归档写入
- 方法:
Close() error - 注意:
- 必须调用,否则 zip 文件不完整
- 会写入中央目录和结束记录
- 应该在 defer 中调用确保关闭
- 示例:
w := zip.NewWriter(file) defer w.Close() // 确保关闭 // 写入文件...
-
-
示例(完整)
package main import ( "archive/zip" "fmt" "os" ) func main() { // 创建 zip 文件 file, err := os.Create("archive.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() // 创建 zip 写入器 w := zip.NewWriter(file) defer w.Close() // 写入文件 fw, _ := w.Create("hello.txt") fw.Write([]byte("Hello, World!")) fmt.Println("zip 归档创建成功") } -
使用场景示例
-
创建多个文件
- 示例:
w := zip.NewWriter(file) defer w.Close() // 文件 1 fw1, _ := w.Create("file1.txt") fw1.Write([]byte("content1")) // 文件 2 fw2, _ := w.Create("file2.txt") fw2.Write([]byte("content2"))
- 示例:
-
创建目录结构
- 示例:
// 创建目录 dirHeader := &zip.FileHeader{Name: "docs/"} w.CreateHeader(dirHeader) // 创建目录中的文件 fw, _ := w.Create("docs/readme.txt") fw.Write([]byte("content"))
- 示例:
-
设置压缩方法
- 示例:
header := &zip.FileHeader{ Name: "data.txt", Method: zip.Deflate, // 使用压缩 } fw, _ := w.CreateHeader(header) fw.Write(data)
- 示例:
-
写入大文件
- 示例:
header := &zip.FileHeader{ Name: "large.bin", Method: zip.Deflate, } fw, _ := w.CreateHeader(header) // 分块写入 buf := make([]byte, 1024*1024) for { n, _ := src.Read(buf) if n == 0 { break } fw.Write(buf[:n]) }
- 示例:
-
-
注意事项
-
忘记 Close
- 错误示例:
w := zip.NewWriter(file) // 写入文件... // 忘记 Close,zip 文件损坏!
- 错误示例:
-
正确的 defer 用法
- 正确示例:
w := zip.NewWriter(file) defer w.Close() // 确保关闭 // 写入文件...
- 正确示例:
-
路径分隔符
- 注意:
- 始终使用正斜杠(/)
- Windows 路径需要转换
- 示例:
name := filepath.ToSlash(relPath) header.Name = name
- 注意:
-
🔹 函数
打开 zip 文件
zip.OpenReader(name string) (*ReadCloser, error)
-
示例
package main import ( "archive/zip" "fmt" "io" ) func main() { // 打开 zip 文件 rc, err := zip.OpenReader("test.zip") if err != nil { fmt.Println("打开失败:", err) return } defer rc.Close() // 遍历所有文件 for _, f := range rc.File { fmt.Printf("文件:%s\n", f.Name) // 跳过目录 if f.FileInfo().IsDir() { continue } // 读取内容 rc, err := f.Open() if err != nil { fmt.Println("打开文件失败:", err) continue } content, _ := io.ReadAll(rc) rc.Close() fmt.Printf(" 内容:%s\n", string(content)) } }
创建 zip 读取器
zip.NewReader(r io.ReaderAt, size int64) (*Reader, error)
-
示例
package main import ( "archive/zip" "bytes" "fmt" "os" ) func main() { // 读取 zip 文件到内存 data, err := os.ReadFile("test.zip") if err != nil { fmt.Println("读取失败:", err) return } // 创建 ReaderAt readerAt := bytes.NewReader(data) // 创建 zip 读取器 r, err := zip.NewReader(readerAt, int64(len(data))) if err != nil { fmt.Println("创建失败:", err) return } fmt.Printf("文件数量:%d\n", len(r.File)) // 遍历文件 for _, f := range r.File { fmt.Printf("文件:%s\n", f.Name) } }
创建 zip 写入器
zip.NewWriter(w io.Writer) *zip.Writer
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { // 创建文件 file, err := os.Create("new.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() // 创建 zip 写入器 w := zip.NewWriter(file) defer w.Close() // 添加文件 fw, _ := w.Create("readme.txt") fw.Write([]byte("这是一个测试文件")) // 添加目录 w.Create("docs/") fmt.Println("zip 归档创建成功") }
🔹 zip.File 方法
打开文件读取
(*zip.File).Open() (io.ReadCloser, error)
-
示例
package main import ( "archive/zip" "fmt" "io" "os" ) func main() { rc, err := zip.OpenReader("test.zip") if err != nil { fmt.Println("打开失败:", err) return } defer rc.Close() // 查找特定文件 for _, f := range rc.File { if f.Name == "config.txt" { // 打开文件 rc, err := f.Open() if err != nil { fmt.Println("打开失败:", err) return } defer rc.Close() // 读取内容 content, err := io.ReadAll(rc) if err != nil { fmt.Println("读取失败:", err) return } fmt.Printf("config.txt 内容:\n%s\n", string(content)) return } } fmt.Println("文件未找到") }
设置密码(已废弃)
(*zip.File).SetPassword(password string)
-
说明:Go 1.17+ 已废弃,不再支持密码功能
-
示例
// 注意:此方法已废弃,不推荐使用 // 如需加密,请使用第三方库
🔹 zip.FileHeader 方法
创建文件头
FileInfoHeader(fi FileInfo, link string) (*FileHeader, error)
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { // 获取文件信息 info, err := os.Stat("test.txt") if err != nil { fmt.Println("获取失败:", err) return } // 创建文件头 header, err := zip.FileInfoHeader(info, "") if err != nil { fmt.Println("创建失败:", err) return } // 设置压缩方法 header.Method = zip.Deflate fmt.Printf("文件名:%s\n", header.Name) fmt.Printf("大小:%d\n", header.UncompressedSize64) fmt.Printf("压缩方法:%d\n", header.Method) }
🔹 zip.Writer 方法
创建文件
(*zip.Writer).Create(name string) (io.Writer, error)
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { file, err := os.Create("create.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() w := zip.NewWriter(file) defer w.Close() // 创建文件 fw, err := w.Create("hello.txt") if err != nil { fmt.Println("创建失败:", err) return } // 写入内容 _, err = fw.Write([]byte("Hello, World!")) if err != nil { fmt.Println("写入失败:", err) return } fmt.Println("文件创建成功") }
创建带选项的文件
(*zip.Writer).CreateHeader(fh *FileHeader) (io.Writer, error)
-
示例
package main import ( "archive/zip" "fmt" "os" "time" ) func main() { file, err := os.Create("header.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() w := zip.NewWriter(file) defer w.Close() // 创建自定义文件头 header := &zip.FileHeader{ Name: "custom.txt", Method: zip.Deflate, Modified: time.Now(), } // 创建文件 fw, err := w.CreateHeader(header) if err != nil { fmt.Println("创建失败:", err) return } // 写入内容 fw.Write([]byte("自定义头文件")) fmt.Println("创建成功") }
创建目录
(*zip.Writer).CreateHeader(fh *FileHeader) (io.Writer, error)
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { file, err := os.Create("dir.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() w := zip.NewWriter(file) defer w.Close() // 创建目录(名称以 / 结尾) dirHeader := &zip.FileHeader{ Name: "mydir/", } _, err = w.CreateHeader(dirHeader) if err != nil { fmt.Println("创建目录失败:", err) return } // 在目录中创建文件 fw, _ := w.Create("mydir/file.txt") fw.Write([]byte("目录中的文件")) fmt.Println("目录创建成功") }
设置注释
(*zip.Writer).SetComment(comment string) error
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { file, err := os.Create("comment.zip") if err != nil { fmt.Println("创建失败:", err) return } defer file.Close() w := zip.NewWriter(file) defer w.Close() // 添加文件 fw, _ := w.Create("readme.txt") fw.Write([]byte("内容")) // 设置注释 err = w.SetComment("这是一个测试 zip 文件") if err != nil { fmt.Println("设置注释失败:", err) return } fmt.Println("注释设置成功") }
关闭写入器
(*zip.Writer).Close() error
-
说明:完成 zip 归档写入,必须调用
-
示例
package main import ( "archive/zip" "fmt" "os" ) func main() { file, err := os.Create("final.zip") if err != nil { fmt.Println("创建失败:", err) return } w := zip.NewWriter(file) // 添加文件 fw, _ := w.Create("test.txt") fw.Write([]byte("hello")) // 关闭写入器(重要!) if err := w.Close(); err != nil { fmt.Println("关闭失败:", err) return } file.Close() fmt.Println("zip 归档完成") }
🔹 实际应用示例
创建 zip 归档
-
示例
package main import ( "archive/zip" "fmt" "io" "io/fs" "os" "path/filepath" "strings" ) func createZip(source, target string) error { // 创建 zip 文件 zipFile, err := os.Create(target) if err != nil { return err } defer zipFile.Close() w := zip.NewWriter(zipFile) defer w.Close() // 遍历源目录 return filepath.Walk(source, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } // 获取相对路径 relPath, err := filepath.Rel(source, path) if err != nil { return err } // 跳过根目录 if relPath == "." { return nil } // 创建文件头 header, err := zip.FileInfoHeader(info) if err != nil { return err } // 设置相对路径 header.Name = filepath.ToSlash(relPath) header.Method = zip.Deflate // 使用压缩 // 如果是目录,添加 / 后缀 if info.IsDir() { header.Name += "/" } // 写入文件头 fw, err := w.CreateHeader(header) if err != nil { return err } // 跳过目录 if info.IsDir() { return nil } // 读取并写入文件内容 file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(fw, file) return err }) } func main() { err := createZip("./source", "./backup.zip") if err != nil { fmt.Println("创建失败:", err) return } fmt.Println("zip 归档创建成功") }
解压 zip 归档
-
示例
package main import ( "archive/zip" "fmt" "io" "os" "path/filepath" "strings" ) func extractZip(zipFile, dest string) error { // 打开 zip 文件 r, err := zip.OpenReader(zipFile) if err != nil { return err } defer r.Close() // 遍历所有文件 for _, f := range r.File { // 构建目标路径 target := filepath.Join(dest, f.Name) // 安全检查:防止路径遍历攻击 if !strings.HasPrefix(target, dest) { fmt.Printf("跳过不安全路径:%s\n", target) continue } // 处理目录 if f.FileInfo().IsDir() { if err := os.MkdirAll(target, f.Mode()); err != nil { return err } continue } // 创建父目录 if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { return err } // 打开 zip 中的文件 rc, err := f.Open() if err != nil { return err } defer rc.Close() // 创建目标文件 outFile, err := os.Create(target) if err != nil { return err } // 复制内容 _, err = io.Copy(outFile, rc) outFile.Close() if err != nil { return err } // 设置权限 os.Chmod(target, f.Mode()) } return nil } func main() { err := extractZip("./backup.zip", "./restored") if err != nil { fmt.Println("解压失败:", err) return } fmt.Println("解压成功") }
向 zip 添加文件
-
示例
package main import ( "archive/zip" "fmt" "io" "os" ) func addFileToZip(zipPath, filePath, archivePath string) error { // 读取现有 zip 文件 existingZip, err := os.Open(zipPath) if err != nil { // 文件不存在,创建新的 return createNewZip(zipPath, filePath, archivePath) } existingZip.Close() // 读取现有 zip 内容 r, err := zip.OpenReader(zipPath) if err != nil { return err } defer r.Close() // 创建临时 zip 文件 tempFile, err := os.Create("temp.zip") if err != nil { return err } defer tempFile.Close() w := zip.NewWriter(tempFile) defer w.Close() // 复制现有文件 for _, f := range r.File { err := copyZipFile(w, f) if err != nil { return err } } // 添加新文件 err = addNewFile(w, filePath, archivePath) if err != nil { return err } w.Close() r.Close() // 替换原文件 os.Remove(zipPath) os.Rename("temp.zip", zipPath) return nil } func createNewZip(zipPath, filePath, archivePath string) error { file, err := os.Create(zipPath) if err != nil { return err } defer file.Close() w := zip.NewWriter(file) defer w.Close() return addNewFile(w, filePath, archivePath) } func copyZipFile(w *zip.Writer, f *zip.File) error { header, _ := zip.FileInfoHeader(f.FileInfo(), "") header.Name = f.Name header.Method = f.Method fw, err := w.CreateHeader(header) if err != nil { return err } if f.FileInfo().IsDir() { return nil } rc, err := f.Open() if err != nil { return err } defer rc.Close() _, err = io.Copy(fw, rc) return err } func addNewFile(w *zip.Writer, filePath, archivePath string) error { file, err := os.Open(filePath) if err != nil { return err } defer file.Close() info, err := file.Stat() if err != nil { return err } header := &zip.FileHeader{ Name: archivePath, Method: zip.Deflate, Modified: info.ModTime(), } fw, err := w.CreateHeader(header) if err != nil { return err } _, err = io.Copy(fw, file) return err } func main() { err := addFileToZip("./archive.zip", "./newfile.txt", "docs/newfile.txt") if err != nil { fmt.Println("添加失败:", err) return } fmt.Println("文件添加成功") }
读取 zip 中的特定文件
-
示例
package main import ( "archive/zip" "fmt" "io" "os" "strings" ) func readFileFromZip(zipPath, fileName string) ([]byte, error) { r, err := zip.OpenReader(zipPath) if err != nil { return nil, err } defer r.Close() // 查找文件 for _, f := range r.File { if f.Name == fileName || strings.HasSuffix(f.Name, fileName) { // 跳过目录 if f.FileInfo().IsDir() { return nil, fmt.Errorf("目标是目录:%s", fileName) } // 打开文件 rc, err := f.Open() if err != nil { return nil, err } defer rc.Close() // 读取内容 return io.ReadAll(rc) } } return nil, fmt.Errorf("文件未找到:%s", fileName) } func main() { content, err := readFileFromZip("./archive.zip", "config.txt") if err != nil { fmt.Println("读取失败:", err) return } fmt.Printf("文件内容:\n%s\n", string(content)) }
列出 zip 归档内容
-
示例
package main import ( "archive/zip" "fmt" "os" "strings" ) func listZipContents(zipPath string) error { r, err := zip.OpenReader(zipPath) if err != nil { return err } defer r.Close() fmt.Printf("%-40s %-8s %-10s %-10s %s\n", "文件名", "类型", "压缩后", "原始", "压缩率") fmt.Println(strings.Repeat("-", 90)) var totalCompressed, totalUncompressed uint64 for _, f := range r.File { // 确定类型 fileType := "文件" if f.FileInfo().IsDir() { fileType = "目录" } // 计算压缩率 var ratio string if f.FileInfo().IsDir() { ratio = "-" } else { ratio = fmt.Sprintf("%.1f%%", float64(f.CompressedSize64)*100/float64(f.UncompressedSize64)) totalCompressed += f.CompressedSize64 totalUncompressed += f.UncompressedSize64 } fmt.Printf("%-40s %-8s %-10d %-10d %s\n", f.Name, fileType, f.CompressedSize64, f.UncompressedSize64, ratio) } fmt.Println(strings.Repeat("-", 90)) if totalUncompressed > 0 { totalRatio := float64(totalCompressed) * 100 / float64(totalUncompressed) fmt.Printf("总计:%d -> %d (压缩率:%.1f%%)\n", totalUncompressed, totalCompressed, totalRatio) } return nil } func main() { err := listZipContents("./backup.zip") if err != nil { fmt.Println("列出失败:", err) return } }
压缩和解压完整示例
-
示例
package main import ( "archive/zip" "fmt" "io" "io/fs" "os" "path/filepath" "strings" ) // 压缩目录 func compressDir(source, target string) error { zipFile, err := os.Create(target) if err != nil { return err } defer zipFile.Close() w := zip.NewWriter(zipFile) defer w.Close() return filepath.Walk(source, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } relPath, err := filepath.Rel(source, path) if err != nil { return err } if relPath == "." { return nil } header, err := zip.FileInfoHeader(info) if err != nil { return err } header.Name = filepath.ToSlash(relPath) header.Method = zip.Deflate if info.IsDir() { header.Name += "/" } fw, err := w.CreateHeader(header) if err != nil { return err } if info.IsDir() { return nil } file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(fw, file) return err }) } // 解压 zip 文件 func decompressZip(zipFile, dest string) error { r, err := zip.OpenReader(zipFile) if err != nil { return err } defer r.Close() for _, f := range r.File { target := filepath.Join(dest, f.Name) if !strings.HasPrefix(target, dest) { continue } if f.FileInfo().IsDir() { os.MkdirAll(target, f.Mode()) continue } os.MkdirAll(filepath.Dir(target), 0755) rc, err := f.Open() if err != nil { return err } outFile, err := os.Create(target) if err != nil { rc.Close() return err } _, err = io.Copy(outFile, rc) outFile.Close() rc.Close() if err != nil { return err } os.Chmod(target, f.Mode()) } return nil } func main() { // 压缩 fmt.Println("压缩目录...") err := compressDir("./source", "./backup.zip") if err != nil { fmt.Println("压缩失败:", err) return } fmt.Println("压缩成功") // 解压 fmt.Println("\n解压文件...") err = decompressZip("./backup.zip", "./restored") if err != nil { fmt.Println("解压失败:", err) return } fmt.Println("解压成功") }
创建自解压 zip(高级)
-
示例
package main import ( "archive/zip" "fmt" "io" "os" ) // 创建带自解压头的 zip(Windows) func createSFXZip(files []string, outputPath string) error { // 读取 SFX 模块(需要单独的 sfx.exe 文件) sfxModule, err := os.ReadFile("sfx.exe") if err != nil { return fmt.Errorf("需要 sfx.exe 模块:%v", err) } // 创建输出文件 outFile, err := os.Create(outputPath) if err != nil { return err } defer outFile.Close() // 写入 SFX 模块 outFile.Write(sfxModule) // 创建 zip 写入器 w := zip.NewWriter(outFile) defer w.Close() // 添加文件 for _, filePath := range files { file, err := os.Open(filePath) if err != nil { return err } info, err := file.Stat() if err != nil { file.Close() return err } header, err := zip.FileInfoHeader(info) if err != nil { file.Close() return err } header.Method = zip.Deflate fw, err := w.CreateHeader(header) if err != nil { file.Close() return err } io.Copy(fw, file) file.Close() } return nil } func main() { files := []string{"app.exe", "config.ini", "readme.txt"} err := createSFXZip(files, "installer.exe") if err != nil { fmt.Println("创建失败:", err) return } fmt.Println("自解压文件创建成功") fmt.Println("注意:需要 sfx.exe 模块才能创建真正的自解压文件") }
🔥 总结
核心类型
- zip.File 👉 zip 中的文件
- zip.FileHeader 👉 文件头信息
- zip.Reader 👉 读取 zip 归档
- zip.ReadCloser 👉 可关闭的读取器
- zip.Writer 👉 写入 zip 归档
常用函数
- zip.OpenReader() 👉 打开 zip 文件
- zip.NewReader() 👉 创建读取器
- zip.NewWriter() 👉 创建写入器
压缩方法
- zip.Store 👉 不压缩(方法 0)
- zip.Deflate 👉 Deflate 压缩(方法 8)
关键方法
- Open() 👉 打开文件读取
- Create() 👉 创建文件
- CreateHeader() 👉 自定义创建文件
- SetComment() 👉 设置注释
- Close() 👉 关闭写入器
- FileInfoHeader() 👉 从 FileInfo 创建头
实际应用场景
- 文件打包和分发
- 备份和归档
- 软件安装包
- 文档压缩传输
- 日志归档
- 资源打包(游戏、应用资源)
与 tar 的区别
- zip:跨平台、支持压缩、自带目录结构
- tar:Unix 标准、通常配合 gzip 使用、保留更多文件属性