Работа с 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)