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

Сжатие и распаковка ZIP в Node.js с Archiver и Unzipper

6 min read Разработка Обновлено 28 Dec 2025
ZIP в Node.js: Archiver и Unzipper
ZIP в Node.js: Archiver и Unzipper

Коробка с папками и листами бумаги, символ архивирования файлов

Почему стоит сжимать файлы

Сжатие файлов уменьшает объём данных для хранения и передачи, упрощает организацию и может повысить безопасность при использовании шифрования. Кратко — причины сжатия:

  • Экономия пространства на носителях
  • Быстрая передача по сети (меньше байт — меньше задержка и трафик)
  • Структурирование и упаковка связанных файлов
  • Возможность проверки целостности и версионирования
  • Парольная защита и шифрование (с ограничениями)

Важно: сжатие не всегда выгодно. Уже сжатые форматы (JPEG, MP4, PDF с изображениями) часто не уменьшаются заметно и требуют дополнительных CPU-ресурсов.

Коротко о пакетах Archiver и Unzipper

Определения в одну строку:

  • Archiver — библиотека для потоковой генерации архивов (ZIP/TAR/GZIP) в Node.js.
  • Unzipper — библиотека для потоковой распаковки ZIP в Node.js.

Archiver интегрируется с модулем fs и поддерживает настройку уровня сжатия, добавление файлов/папок и создание многотомных архивов. Unzipper позволяет извлекать содержимое ZIP простым потоком и поддерживает промисы.

Когда использовать Archiver + Unzipper

  • Нужна потоковая обработка (не загружать большие файлы в память)
  • Требуется гибкое добавление файлов и каталогов
  • Нужна совместимость с модулем fs и стандартными потоками Node.js

Быстрая настройка проекта

  1. Создайте проект и инициализируйте npm:
mkdir node-zip-archiver
cd node-zip-archiver
npm init -y
  1. Установите зависимости:
npm install archiver unzipper --save
  1. Создайте файл, например app.js.

Создание ZIP из файла

Ниже — функция, которая принимает имя файла и создаёт ZIP-архив с тем же именем и расширением .zip.

const archiver = require('archiver')
const fs = require('fs')

// создать ZIP из файла
const createZipFromFile = (file) => {
    const filePath = __dirname + '/' + file
    const output = fs.createWriteStream(filePath + '.zip')
    const archive = archiver('zip', {
        zlib: { level: 9 } // установить максимальный уровень сжатия
    })

    archive.pipe(output);
    archive.file(filePath, { name: file })
    archive.finalize()
}

Пояснения:

  • archive.file(…) добавляет конкретный файл в архив;
  • опция name управляет именем файла внутри архива (без исходного пути);
  • zlib.level — баланс скорость/сжатие (0–9).

Создание ZIP из папки

Для каталогов используется метод directory. Параметр false означает: включить только содержимое папки, без корневой папки; true — включить саму папку.

// создать ZIP из папки
const createZipFromFolder = (folder) => {
    const folderPath = __dirname + '/' + folder
    const output = fs.createWriteStream(folderPath + '.zip')

    const archive = archiver('zip', {
        zlib: { level: 9 } // установить максимальный уровень сжатия
    })

    archive.pipe(output)
    archive.directory(folderPath, false)
    archive.finalize()
}

Рекомендация по флагу:

  • Если вы распаковываете архив в общую директорию, включение корневой папки (true) помогает избежать ‘захламления’ текущей папки.

Распаковка ZIP

Пример функции распаковки с использованием unzipper. Функция асинхронная и создаёт папку extracted рядом с текущим файлом, если её нет.

const unzipper = require('unzipper')
const fs = require('fs')

// функция для извлечения ZIP
const extractZip = async (file) => {
    const filePath = __dirname + '/' + file
    const outputPath = __dirname + '/extracted'
    if (!fs.existsSync(outputPath)) {
        fs.mkdirSync(outputPath)
    }
    await fs.createReadStream(filePath)
        .pipe(unzipper.Extract({ path: outputPath }))
        .promise()
}

Примечание: unzipper.Extract создаёт директории автоматически при необходимости.

Пример тестового запуска

Небольшой IIFE для проверки функций:

(async function () {
    const file = 'test.pdf'
    const folder = 'test_folder'
    const zipFile = 'test.pdf.zip'
    createZipFromFile(file)
    console.log('ZIP archive successfully created from file')
    createZipFromFolder(folder)
    console.log('ZIP archive successfully created from folder')
    await extractZip(zipFile)
    console.log('ZIP archive extracted successfully')
})()

Этот код сразу вызывается и позволит быстро проверить рабочие сценарии.

Когда сжатие не помогает: контрпримеры

  • Форматы уже сжатых данных: JPEG, PNG (оптимизированные), MP4, MPEG, многие PDF-файлы — дополнительные попытки сжать могут не дать выигрыша и только потратят CPU.
  • Низкая пропускная способность CPU: если сервер имеет слабый процессор, агрессивное сжатие (level 9) замедлит обработку быстрее, чем выигрыш по сети.
  • Мелкие файлы: упаковка тысяч мелких файлов в один архив может уменьшить overhead при передаче, но ухудшит параллелизм и даст больше задержек для частичного доступа.

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

  • node: использовать встроенный модуль zlib для GZIP- или DEFLATE-потоков (не архивирует несколько файлов в контейнер).
  • adm-zip: просто использовать для быстрых операций с ZIP в памяти (подходит для небольших архивов).
  • yauzl и node-stream-zip: библиотеки для чтения ZIP с низким потреблением памяти и контролем над извлечением.

Выбор зависит от требования: потоковая обработка — Archiver + Unzipper; манипуляции в памяти — adm-zip.

Производительность и эвристики

  • Уровень сжатия: компромисс между скоростью и размером. По умолчанию используйте уровень 6–8 в продакшене; 9 только для фоновых задач.
  • Потоки: всегда используйте потоковые операции, если файлы > 50–100 МБ, чтобы не задействовать ОЗУ.
  • Параллелизм: пакетуйте логически связанные файлы вместе; избегайте создания сотен маленьких архивов в одной итерации.

Правило большого пальца: если ожидаемые выгоды по трафику меньше затрат CPU/времени — не стоит сжимать.

Безопасность и целостность

  • ZIP-шифрование по стандарту PKZIP считается слабым. Для конфиденциальных данных используйте внешнее шифрование (AES-GCM) перед упаковкой или шифруйте содержимое с помощью проверенных библиотек.
  • Контроль целостности: храните и/или передавайте контрольные суммы (SHA-256) для критичных файлов и архивов.
  • Паранойя в открытии архивов: не распаковывайте загруженные из ненадёжных источников архивы на продакшн-серверах без проверки путей (zip-slip атак). Всегда проверяйте пути файлов внутри архива на предмет выхода за ожидаемую директорию.

Пример проверки zip-slip (псевдокод):

// Перед записью файла из архива проверить
const targetPath = path.join(outputPath, entryPath)
if (!targetPath.startsWith(path.resolve(outputPath) + path.sep)) {
    throw new Error('Небезопасный путь в архиве')
}

Контрольные списки по ролям

Для разработчика:

  • написать модуль-обёртку вокруг archiver/unzipper
  • покрыть юнит-тестами (создание/распаковка, именифеста, целостности)
  • добавить логирование и таймауты

Для DevOps:

  • мониторинг CPU при массовом сжатии
  • планирование задач сжатия в off-peak
  • резервирование диска и очистка временных файлов

Для QA:

  • тесты с большими файлами (>1 ГБ)
  • тесты zip-slip и некорректных архивов
  • тесты совместимости с другими клиентами (Windows, macOS, Linux)

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

  • Архивы успешно создаются из заданных файлов/папок
  • Распаковка возвращает байтово-идентичное содержимое (для бинарных файлов) или ожидаемую структуру
  • Нет утечек памяти при обработке больших файлов (инструмент — heap snapshot)
  • Защита от zip-slip: все пути внутри архива валидированы

Тестовые случаи и приёмаемые критерии

  1. Создание архива из одного большого файла (1–5 ГБ) без OOM — пройдено.
  2. Создание архива из папки со вложенностью до 10 уровней — структура сохранена.
  3. Распаковка архива с некорректными именами (../../) — блокируется.
  4. Сравнение контрольной суммы: исходный файл и распакованный равны (SHA-256).

Отладка и распространённые ошибки

  • Ошибка: EBUSY / EPERM при записи ZIP — проверьте права и что файл не открыт другой программой.
  • Ошибка: ENOENT при архивировании — проверьте корректность путей и рабочую директорию (__dirname).
  • Проблема: архив создался, но пустой — убедитесь, что archive.finalize() вызывается и что записи готовы к pipe.

Логирование: добавьте прослушку событий archiver (archive.on('warning', ...), archive.on('error', ...)) и stream (output.on('close', ...)) для диагностики.

Совместимость и миграция

  • Node.js: Archiver и Unzipper работают с Node 8+ (проверьте минимальные требования в package.json пакетов). Для поддержки старых версий Node может потребоваться полифилл потоков.
  • Платформы: ZIP — кроссплатформенный формат; однако проверяйте специфические атрибуты (права файлов, символические ссылки) при переносе между Linux и Windows.

Совет по миграции: создайте тестовый набор архивов и прогони его на целевых платформах.

Шаблон SOP для пакетной обработки файлов

  1. Сбор входных файлов в временной директории.
  2. Генерация контрольных сумм для каждого файла.
  3. Создание архива с уровнем сжатия, выбранным согласно политике (напр., 6).
  4. Перемещение архива в хранилище и проверка checksum архива.
  5. Очистка временной директории.
  6. Мониторинг завершённых задач и алерты при ошибках.

Короткий глоссарий

  • Архив — контейнер для одного или нескольких файлов (например, .zip).
  • Stream — потоковое чтение/запись данных в Node.js.
  • ZIP-slip — уязвимость, когда файл из архива распаковывается за пределы целевой директории.

Примеры практических сценариев

  • Веб-сервис генерирует бэкап проекта: упаковать папку проекта с исключением node_modules, добавить manifest.json с метаданными и контрольной суммой.
  • Экспорт данных: собрать CSV-файлы отчётов в архив и отдать пользователю одной ссылкой.
  • Обмен мультимедиа между сервисами: предварительно сжать, затем передать по S3/HTTP.

Резюме

Сжатие ZIP в Node.js с помощью archiver и unzipper — простой и надёжный путь для организации хранения и передачи файлов. Архивы удобны, но требуют внимания к безопасности (zip-slip, шифрование), производительности (уровень сжатия) и совместимости. Выберите потоковый подход для больших объёмов данных и используйте контрольные суммы для гарантии целостности.

Important: всегда тестируйте pipeline с реальными объёмами и типами данных, которые будут в продакшне.

Ключевые ссылки: документация пакетов archiver и unzipper в npm (по имени пакета).

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

Чёрный экран веб‑камеры в Windows — быстрое устранение
Технологии

Чёрный экран веб‑камеры в Windows — быстрое устранение

Списки и чек-листы в Microsoft Word
Руководство

Списки и чек-листы в Microsoft Word

Amahi — домашний сервер на Linux
Домашний сервер

Amahi — домашний сервер на Linux

Удалить или деактивировать аккаунт LinkedIn
Социальные сети

Удалить или деактивировать аккаунт LinkedIn

Лучшие IPTV‑приложения для Android и Android TV
IPTV

Лучшие IPTV‑приложения для Android и Android TV

Как очистить кэш Windows и освободить место
Windows

Как очистить кэш Windows и освободить место