Гид по технологиям

Сжатие в Go: gzip и zlib — руководство

5 min read Go Обновлено 28 Dec 2025
Сжатие в Go: gzip и zlib — руководство
Сжатие в Go: gzip и zlib — руководство

Маскот Go — синий гофер на фоне колонн зелёных символов.

Кратко о gzip и zlib

  • gzip — формат файла + утилита; подходит для упаковки одиночных файлов (HTML, CSS, JS и т. п.). Включает метаданные и контрольную сумму.
  • zlib — библиотечная обёртка над алгоритмом Deflate, часто используется внутри других форматов (PNG, HTTP/Transfer-Encoding).
  • Оба используют Deflate (LZ77 + Huffman), но имеют разные заголовки и концы пакета.

Важно: gzip обычно даёт лучшую степень сжатия, но требует больше CPU при сжатии и распаковке. zlib проще для потоковых операций и встраивания в протоколы.

Пакет compress в Go

Структура пакета compress в Go.

Пакет compress содержит подмодули, в том числе compress/gzip и compress/zlib. Оба предоставляют Reader/Writer интерфейсы, совместимые с io.Reader/io.Writer. Импортируйте как обычно:

import (
    "compress/gzip" // gzip: формат файла RFC 1952
    "compress/zlib" // zlib: библиотечный формат
)

Пояснение: gzip.NewWriter и gzip.NewReader работают с файловыми потоками, zlib.NewWriterLevel и zlib.NewReader — с потоками и позволяют задать уровень компрессии.

Сжатие файла с помощью gzip

Шаги:

  1. Откройте исходный файл.
  2. Создайте новый файл с расширением .gz.
  3. Создайте gzip.Writer поверх файла.
  4. Скопируйте данные из исходного файла в gzip.Writer.
  5. Закройте writer (Close автоматически вызывает Flush и записывает окончание архива).

Пример (Go):

package main

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

func main() {
    // Открываем исходный файл
    originalFile, err := os.Open("example.txt")
    if err != nil {
        panic(err)
    }
    defer originalFile.Close()

    // Создаём gz-файл
    gzippedFile, err := os.Create("example.txt.gz")
    if err != nil {
        panic(err)
    }
    defer gzippedFile.Close()

    // Создаём gzip.Writer
    gzipWriter := gzip.NewWriter(gzippedFile)
    defer gzipWriter.Close()

    // Копируем данные
    if _, err := io.Copy(gzipWriter, originalFile); err != nil {
        panic(err)
    }

    // Дополнительно можно явно вызвать Flush(), но Close() гарантирует корректное завершение
    if err := gzipWriter.Flush(); err != nil {
        panic(err)
    }
}

Советы по надёжности:

  • Закрывайте writer через defer, чтобы гарантировать запись заголовков/контролных сумм.
  • Flush полезен для долгих потоков, чтобы сбросить буферы без закрытия.

Короткая команда для теста в Unix (создаёт файл example.txt и записывает строку):

# Создаёт текстовый файл
touch example.txt

# Пишет строку в файл
echo 'Hello, world!' > example.txt

Распаковка gzip-файла

Алгоритм обратим: откройте .gz, создайте gzip.Reader, затем скопируйте данные в новый файл.

package main

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

func main() {
    gzippedFile, err := os.Open("example.txt.gz")
    if err != nil {
        panic(err)
    }
    defer gzippedFile.Close()

    gzipReader, err := gzip.NewReader(gzippedFile)
    if err != nil {
        panic(err)
    }
    defer gzipReader.Close()

    uncompressedFile, err := os.Create("example_uncompressed.txt")
    if err != nil {
        panic(err)
    }
    defer uncompressedFile.Close()

    if _, err := io.Copy(uncompressedFile, gzipReader); err != nil {
        panic(err)
    }
}

Сжатие и распаковка с помощью zlib

zlib чаще используется для потоковой передачи данных или встраивания в контейнеры. Принцип тот же, но есть возможность задавать уровень сжатия.

Пример сжатия в файл example.zlib с максимальным уровнем:

package main

import (
    "compress/zlib"
    "io"
    "os"
)

func main() {
    file, err := os.Create("example.zlib")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    writer, err := zlib.NewWriterLevel(file, zlib.BestCompression)
    if err != nil {
        panic(err)
    }
    defer writer.Close()

    inputFile, err := os.Open("example.txt")
    if err != nil {
        panic(err)
    }
    defer inputFile.Close()

    if _, err := io.Copy(writer, inputFile); err != nil {
        panic(err)
    }
}

Распаковка zlib-файла в stdout:

package main

import (
    "compress/zlib"
    "io"
    "os"
)

func main() {
    file, err := os.Open("compressed_file.zlib")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    reader, err := zlib.NewReader(file)
    if err != nil {
        panic(err)
    }
    defer reader.Close()

    if _, err := io.Copy(os.Stdout, reader); err != nil {
        panic(err)
    }
}

Когда выбирать gzip, а когда zlib

  • Выберите gzip, если нужно: одиночный файл, переносимость на уровень файловой системы, встроенные метаданные и CRC.
  • Выберите zlib, если нужно: встроить сжатые данные в протокол/формат, работать со стримами, управлять уровнями сжатия.

Примеры: веб-серверы часто отдают HTTP-ответы в gzip; внутренние бинарные форматы или сетевые протоколы могут использовать zlib-обёртку.

Практические рекомендации и лучшие практики

  • Баланс скорость ↔ сжатие: используйте zlib.DefaultCompression или gzip.DefaultCompression для быстрого результата; zlib.BestCompression/gzip.BestCompression для максимальной плотности.
  • Для больших файлов используйте буферизацию и io.Copy; не читаите весь файл в память.
  • В потоках отправляйте периодические Flush() (если используете writer в долгоживущем соединении).
  • Всегда проверяйте ошибки при Open/Create/Read/Write/Close.
  • Для многопоточности держите writer/reader локальным для горутины или синхронизируйте доступ.

Когда эти инструменты не подойдут

  • Если надо архивировать структуру директорий с несколькими файлами и метаданными (права, временные метки), лучше использовать tar+gzip или zip.
  • Для дедупликации и максимального сжатия больших бинарных данных могут подойти специализированные алгоритмы (zstd, brotli) — рассмотрите их.
  • Для шифрования перед сжатием: сначала сжимайте, затем шифруйте. Обратный порядок плохо сжимает данные.

Альтернативные подходы

  • tar + gzip — для архивации каталогов и сохранения метаданных.
  • zip — поддерживает несколько файлов в одном архиве и часто используется в Windows-средах.
  • zstd и brotli — современные алгоритмы с лучшим компромиссом скорость/сжатие в ряде сценариев; доступны как сторонние библиотеки для Go.

Ментальные модели (как думать о компрессии)

  • Данные ≈ энтропия: чем более предсказуемы байты, тем лучше компрессия.
  • Скорость обработки = CPU × данные. Увеличивая степень сжатия, вы платите CPU (и возможно задержкой).
  • Стрим против файла: если нужен поток — используйте zlib; если нужен переносимый файл — gzip.

Критерии приёмки

  • Исходный и распакованный файлы идентичны (контроль целостности).
  • В случае gzip файл открывается стандартными утилитами (gunzip) и соответствует RFC 1952.
  • Используемый уровень компрессии соответствует требованиям по времени/ресурсам.

Чек-листы по ролям

Разработчик:

  • Проверил обработку ошибок при Open/Read/Write/Close.
  • Не держит файлы в памяти целиком.
  • Закрывает writers через defer.

Операционный инженер:

  • Убедился, что лог‑файлы корректно сжимаются и ротация не ломает читаемость.
  • Настроил мониторинг CPU при пиковых задачах сжатия.

Тестировщик:

  • Запустил тесты на больших файлах и на случайных данных.
  • Проверил совместимость с внешними утилитами (gunzip, zlib tools).

Быстрое SOP для автоматизации архивации файлов

  1. Проверить доступность каталога и прав на запись.
  2. Создать временный файл .tmp и писать в него результаты.
  3. Завершить запись и закрыть файл.
  4. Открыть исходный файл и создать gzip.Writer для целевого .gz.
  5. Копировать данные через io.Copy.
  6. Закрыть writer и заменить старый файл атомарно (rename).
  7. Логировать итог операции и размер до/после.

Decision flow (простое руководство выбора)

flowchart TD
  A[Нужно сжать данные?] --> B{Один файл или поток}
  B -->|Один файл| C[gzip]
  B -->|Поток| D[zlib]
  C --> E{Нужна максимальная плотность?}
  E -->|Да| F[gzip.BestCompression]
  E -->|Нет| G[gzip.DefaultCompression]
  D --> H{Нужен контроль уровня?}
  H -->|Да| I[zlib.NewWriterLevel]
  H -->|Нет| J[zlib.NewWriter]

Мини-глоссарий

  • Deflate — алгоритм сжатия на основе LZ77 + Huffman.
  • CRC — контрольная сумма, используемая в gzip для проверки целостности.
  • Level — уровень компрессии (скорость vs размер).

Результат вывода распакованного текста.

Итог

gzip и zlib — надёжные инструменты для сжатия в Go. Выбирайте gzip для файлов и переносимости, zlib — для потоков и встраивания в протоколы. Не забывайте о балансе между скоростью и плотностью компрессии, корректном закрытии writer/reader и тестировании на реальных данных.

Important: всегда проверяйте ошибки и учитывайте ресурсы сервера при массовой компрессии.

Краткие советы по отладке:

  • Если распаковка не проходит — проверьте, корректен ли заголовок и CRC (gzip).
  • Для измерения влияния используйте профилирование CPU и сравнение размеров до/после.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как очистить кэш на Android — быстро и безопасно
Android.

Как очистить кэш на Android — быстро и безопасно

Excel: LEFT, RIGHT и MID — как извлечь текст
Excel

Excel: LEFT, RIGHT и MID — как извлечь текст

Автоудаление OTP в iOS 17
iOS

Автоудаление OTP в iOS 17

Отслеживание новогодних решений в Google Calendar
Productivity

Отслеживание новогодних решений в Google Calendar

Эффекты в реальном времени для Audacity — найти и установить
Аудио

Эффекты в реальном времени для Audacity — найти и установить

Proxmox на Intel NUC: установка и руководство
Виртуализация

Proxmox на Intel NUC: установка и руководство