Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go 语言标准库 —— compress/gzip 包(Gzip 压缩/解压缩)


🔹 概述

compress/gzip 包实现了 RFC 1952 定义的 Gzip 压缩格式。

主要功能:

  • Gzip 格式压缩
  • Gzip 格式解压缩
  • 支持自定义压缩级别
  • 支持文件头信息(文件名、时间戳、注释)
  • 流式处理

重要说明:

  • Gzip 基于 DEFLATE 算法(compress/flate)
  • 添加了文件头和 CRC32 校验
  • 支持 .gz 文件扩展名
  • 广泛应用于 HTTP 压缩、文件压缩、日志存储

压缩级别:

  • NoCompression (0) - 不压缩
  • BestSpeed (1) - 最快压缩
  • BestCompression (9) - 最大压缩
  • DefaultCompression (-1) - 默认压缩
  • HuffmanOnly (-2) - 仅 Huffman 编码

🔹 核心类型

Gzip 写入器(压缩)

gzip.Writer struct

  • 说明:

    • 实现了 io.WriteCloser 接口
    • 将写入的数据进行 Gzip 压缩
    • 自动添加文件头、CRC32 校验
    • 支持自定义文件头信息
  • 创建方式:

    func NewWriter(w io.Writer) *Writer
    func NewWriterLevel(w io.Writer, level int) (*Writer, error)
    
  • 常用方法:

    • Write(p []byte) (n int, err error) - 写入并压缩数据
    • Flush() error - 刷新缓冲区
    • Close() error - 关闭写入器(必须调用)
    • Reset(w io.Writer) - 重置写入器
    • SetHeader(h Header) - 设置文件头
  • 示例:

    package main
    
    import (
        "bytes"
        "compress/gzip"
        "fmt"
    )
    
    func main() {
        var buf bytes.Buffer
        writer := gzip.NewWriter(&buf)
        
        writer.Write([]byte("hello world"))
        writer.Close()
        
        fmt.Printf("压缩后大小:%d 字节\n", buf.Len())
    }
    

Gzip 读取器(解压缩)

gzip.Reader struct

  • 说明:

    • 实现了 io.ReadCloser 接口
    • 从底层读取器读取压缩数据并解压缩
    • 自动处理文件头、CRC32 校验
  • 创建方式:

    func NewReader(r io.Reader) (*Reader, error)
    func NewReaderSize(r io.Reader, size int) (*Reader, error)
    
  • 常用方法:

    • Read(p []byte) (n int, err error) - 读取并解压缩
    • Close() error - 关闭读取器
    • Reset(r io.Reader) - 重置读取器
    • Multistream(ok bool) - 启用/禁用多流模式
  • 示例:

    package main
    
    import (
        "compress/gzip"
        "fmt"
        "io"
        "os"
    )
    
    func main() {
        file, _ := os.Open("data.gz")
        defer file.Close()
        
        reader, _ := gzip.NewReader(file)
        defer reader.Close()
        
        data, _ := io.ReadAll(reader)
        fmt.Printf("解压后大小:%d 字节\n", len(data))
    }
    

🔹 Header 文件头类型

Gzip 文件头

gzip.Header struct

type Header struct {
    Comment string    // 注释信息
    Extra   []byte    // 额外数据
    ModTime time.Time // 修改时间
    Name    string    // 文件名
    OS      byte      // 操作系统类型
}

字段说明:

  • Comment - 文件注释(可选)
  • Extra - 自定义额外数据(可选)
  • ModTime - 修改时间(默认当前时间)
  • Name - 原始文件名(可选)
  • OS - 操作系统类型(默认 2=Unix)

示例:

writer.SetHeader(gzip.Header{
    Name:    "test.txt",
    Comment: "Test file",
    ModTime: time.Now(),
    OS:      2,
})

🔹 使用场景

1. 基础压缩和解压缩

package main

import (
    "bytes"
    "compress/gzip"
    "fmt"
    "io"
)

func main() {
    original := []byte("Hello, World!")
    
    // 压缩
    var compressed bytes.Buffer
    writer := gzip.NewWriter(&compressed)
    writer.Write(original)
    writer.Close()
    
    // 解压缩
    reader, _ := gzip.NewReader(&compressed)
    decompressed, _ := io.ReadAll(reader)
    reader.Close()
    
    fmt.Printf("原始:%d 字节,压缩:%d 字节\n", 
        len(original), compressed.Len())
}

2. 文件压缩

package main

import (
    "compress/gzip"
    "fmt"
    "io"
    "os"
)

func compressFile(src, dst string) error {
    srcFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer srcFile.Close()
    
    dstFile, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer dstFile.Close()
    
    writer := gzip.NewWriter(dstFile)
    _, err = io.Copy(writer, srcFile)
    if err != nil {
        writer.Close()
        return err
    }
    
    return writer.Close()
}

func decompressFile(src, dst string) error {
    srcFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer srcFile.Close()
    
    dstFile, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer dstFile.Close()
    
    reader, err := gzip.NewReader(srcFile)
    if err != nil {
        return err
    }
    defer reader.Close()
    
    _, err = io.Copy(dstFile, reader)
    return err
}

3. HTTP 响应压缩

package main

import (
    "compress/gzip"
    "fmt"
    "net/http"
    "strings"
)

func gzipHandler(w http.ResponseWriter, r *http.Request) {
    if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
        fmt.Fprintln(w, "No gzip support")
        return
    }
    
    w.Header().Set("Content-Encoding", "gzip")
    writer := gzip.NewWriter(w)
    defer writer.Close()
    
    content := strings.Repeat("Compressed content. ", 1000)
    writer.Write([]byte(content))
    writer.Flush()
}

4. 日志压缩存储

package main

import (
    "compress/gzip"
    "fmt"
    "io"
    "os"
    "strings"
    "time"
)

type GzipLogger struct {
    file   *os.File
    writer *gzip.Writer
}

func NewGzipLogger(filename string) (*GzipLogger, error) {
    file, err := os.Create(filename)
    if err != nil {
        return nil, err
    }
    
    writer := gzip.NewWriter(file)
    writer.SetHeader(gzip.Header{
        Name:    filename,
        ModTime: time.Now(),
    })
    
    return &GzipLogger{file, writer}, nil
}

func (gl *GzipLogger) Write(entry string) error {
    _, err := gl.writer.Write([]byte(entry + "\n"))
    if err != nil {
        return err
    }
    return gl.writer.Flush()
}

func (gl *GzipLogger) Close() error {
    if err := gl.writer.Close(); err != nil {
        return err
    }
    return gl.file.Close()
}

func ReadGzipLog(filename string) ([]string, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    reader, err := gzip.NewReader(file)
    if err != nil {
        return nil, err
    }
    defer reader.Close()
    
    data, err := io.ReadAll(reader)
    if err != nil {
        return nil, err
    }
    
    return strings.Split(strings.TrimSpace(string(data)), "\n"), nil
}

5. 创建 .tar.gz 归档

package main

import (
    "archive/tar"
    "compress/gzip"
    "io"
    "os"
    "path/filepath"
)

func createTarGz(filename string, files []string) error {
    file, _ := os.Create(filename)
    defer file.Close()
    
    gw := gzip.NewWriter(file)
    defer gw.Close()
    
    tw := tar.NewWriter(gw)
    defer tw.Close()
    
    for _, f := range files {
        addFileToTar(tw, f)
    }
    return nil
}

func addFileToTar(tw *tar.Writer, filename string) error {
    file, _ := os.Open(filename)
    defer file.Close()
    
    info, _ := file.Stat()
    header, _ := tar.FileInfoHeader(info, "")
    
    tw.WriteHeader(header)
    _, err := io.Copy(tw, file)
    return err
}

func extractTarGz(filename, dest string) error {
    file, _ := os.Open(filename)
    defer file.Close()
    
    reader, _ := gzip.NewReader(file)
    defer reader.Close()
    
    tr := tar.NewReader(reader)
    for {
        header, err := tr.Next()
        if err == io.EOF {
            break
        }
        
        targetPath := filepath.Join(dest, header.Name)
        if header.Typeflag == tar.TypeReg {
            outFile, _ := os.Create(targetPath)
            io.Copy(outFile, tr)
            outFile.Close()
        }
    }
    return nil
}

🔹 错误处理

常见错误

  • 无效的 gzip 格式

    reader, err := gzip.NewReader(file)
    if err != nil {
        fmt.Println("无效的 gzip 格式:", err)
    }
    
  • 未关闭写入器

    writer := gzip.NewWriter(dst)
    defer writer.Close() // 必须调用
    
  • CRC32 校验失败

    data, err := io.ReadAll(reader)
    if err != nil {
        fmt.Println("数据损坏:", err)
    }
    

最佳实践

func safeCompress(src, dst string) (err error) {
    srcFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer srcFile.Close()
    
    dstFile, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer func() {
        dstFile.Close()
        if err != nil {
            os.Remove(dst)
        }
    }()
    
    writer := gzip.NewWriter(dstFile)
    defer writer.Close()
    
    _, err = io.Copy(writer, srcFile)
    return err
}

🔹 性能优化

1. 选择合适的压缩级别

// 速度优先
writer, _ := gzip.NewWriterLevel(dst, gzip.BestSpeed)

// 压缩率优先
writer, _ := gzip.NewWriterLevel(dst, gzip.BestCompression)

// 平衡(推荐)
writer := gzip.NewWriter(dst) // DefaultCompression

2. 对象池复用

var writerPool = sync.Pool{
    New: func() interface{} {
        w, _ := gzip.NewWriterLevel(nil, gzip.DefaultCompression)
        return w
    },
}

func compress(data []byte) ([]byte, error) {
    writer := writerPool.Get().(*gzip.Writer)
    defer writerPool.Put(writer)
    
    var buf bytes.Buffer
    writer.Reset(&buf)
    writer.Write(data)
    writer.Close()
    
    return buf.Bytes(), nil
}

🔥 总结

核心类型

  • gzip.Writer - 压缩写入器(io.WriteCloser)
  • gzip.Reader - 解压缩读取器(io.ReadCloser)

核心函数

  • gzip.NewWriter(w io.Writer) - 创建压缩器
  • gzip.NewWriterLevel(w io.Writer, level int) - 创建压缩器(指定级别)
  • gzip.NewReader(r io.Reader) - 创建解压器

压缩级别

级别说明场景
NoCompression0不压缩已压缩数据
BestSpeed1最快实时传输
DefaultCompression-1默认一般用途
BestCompression9最大压缩归档存储

使用场景

  • 文件压缩 - .gz 文件
  • HTTP 压缩 - 减少带宽
  • 日志存储 - 压缩历史日志
  • 归档备份 - .tar.gz 格式

最佳实践

  • ✅ 始终调用 Close() 完成压缩
  • ✅ 使用 defer 确保资源释放
  • ✅ 选择合适的压缩级别
  • ✅ 使用对象池提高性能
  • ✅ 完善的错误处理
  • ⚠️ 注意:必须调用 Close() 才能写入 CRC32

compress/gzip 包提供了广泛使用的 Gzip 压缩功能,适合文件压缩、HTTP 压缩等各种场景!