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)- 创建解压器
压缩级别
| 级别 | 值 | 说明 | 场景 |
|---|---|---|---|
| NoCompression | 0 | 不压缩 | 已压缩数据 |
| BestSpeed | 1 | 最快 | 实时传输 |
| DefaultCompression | -1 | 默认 | 一般用途 |
| BestCompression | 9 | 最大压缩 | 归档存储 |
使用场景
- 文件压缩 - .gz 文件
- HTTP 压缩 - 减少带宽
- 日志存储 - 压缩历史日志
- 归档备份 - .tar.gz 格式
最佳实践
- ✅ 始终调用 Close() 完成压缩
- ✅ 使用 defer 确保资源释放
- ✅ 选择合适的压缩级别
- ✅ 使用对象池提高性能
- ✅ 完善的错误处理
- ⚠️ 注意:必须调用 Close() 才能写入 CRC32
compress/gzip 包提供了广泛使用的 Gzip 压缩功能,适合文件压缩、HTTP 压缩等各种场景!