Работа с ZIP/архивами в Go (Golang) — полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с ZIP/архивами в Go (Golang)

В Go (Golang) работа с ZIP-архивами реализована через стандартный пакет archive/zip. Этот пакет предоставляет все необходимые инструменты для создания, чтения, записи и извлечения файлов из ZIP-архивов. В отличие от многих других языков, Go не требует установки сторонних библиотек для базовых операций — всё уже есть в стандартной библиотеке.

Пакет archive/zip поддерживает:

  • Создание новых ZIP-архивов
  • Чтение существующих архивов
  • Добавление файлов и директорий
  • Извлечение отдельных файлов или всего архива
  • Работу с метаданными файлов (дата, права доступа)

Создание ZIP-архива в Go

Для создания архива необходимо открыть файл на запись и создать zip.Writer. Каждый файл добавляется через Create или CreateHeader для тонкой настройки.

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
)

func main() {
    // Создаём новый ZIP-файл
    zipFile, err := os.Create("example.zip")
    if err != nil {
        panic(err)
    }
    defer zipFile.Close()

    // Создаём Writer для архива
    zipWriter := zip.NewWriter(zipFile)
    defer zipWriter.Close()

    // Добавляем текстовый файл
    file1, err := zipWriter.Create("hello.txt")
    if err != nil {
        panic(err)
    }
    _, err = file1.Write([]byte("Hello, Go ZIP!"))
    if err != nil {
        panic(err)
    }

    // Добавляем второй файл с содержимым из существующего файла
    sourceFile, err := os.Open("source.txt")
    if err != nil {
        panic(err)
    }
    defer sourceFile.Close()

    targetFile, err := zipWriter.Create("copy_of_source.txt")
    if err != nil {
        panic(err)
    }
    _, err = io.Copy(targetFile, sourceFile)
    if err != nil {
        panic(err)
    }

    fmt.Println("Архив создан: example.zip")
}

Чтение ZIP-архива

Для чтения архива используется zip.OpenReader, который возвращает ReadCloser. Каждый файл представлен структурой zip.File.

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
)

func main() {
    // Открываем архив для чтения
    reader, err := zip.OpenReader("example.zip")
    if err != nil {
        panic(err)
    }
    defer reader.Close()

    // Перебираем все файлы в архиве
    for _, file := range reader.File {
        fmt.Printf("Файл: %s\n", file.Name)
        fmt.Printf("Размер: %d байт\n", file.UncompressedSize64)
        fmt.Printf("Сжатый размер: %d байт\n", file.CompressedSize64)
        fmt.Printf("Дата модификации: %s\n", file.Modified.Format("2006-01-02 15:04:05"))
        fmt.Println("---")

        // Открываем файл из архива
        rc, err := file.Open()
        if err != nil {
            panic(err)
        }
        defer rc.Close()

        // Читаем содержимое (если небольшой файл)
        content, err := io.ReadAll(rc)
        if err != nil {
            panic(err)
        }
        fmt.Printf("Содержимое: %s\n\n", string(content))
    }
}

Извлечение файлов из архива

Для полной распаковки архива в директорию необходимо создать целевые папки и записать содержимое каждого файла.

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
    "path/filepath"
)

func extractZip(src, dest string) error {
    reader, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer reader.Close()

    for _, file := range reader.File {
        // Безопасное имя файла (предотвращение path traversal)
        filePath := filepath.Join(dest, file.Name)
        
        // Проверка на выход за пределы целевой директории
        if !strings.HasPrefix(filepath.Clean(filePath), filepath.Clean(dest)+string(os.PathSeparator)) {
            return fmt.Errorf("недопустимый путь: %s", filePath)
        }

        // Создаём директории
        if file.FileInfo().IsDir() {
            os.MkdirAll(filePath, os.ModePerm)
            continue
        }

        // Создаём родительскую директорию
        os.MkdirAll(filepath.Dir(filePath), os.ModePerm)

        // Открываем файл из архива
        rc, err := file.Open()
        if err != nil {
            return err
        }
        defer rc.Close()

        // Создаём целевой файл
        outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
        if err != nil {
            return err
        }
        defer outFile.Close()

        // Копируем содержимое
        _, err = io.Copy(outFile, rc)                    

Рекомендаций