Сжатие и распаковка на основе Node.js

Node.js

Формат сжатия

zip и gzip — два наиболее распространенных формата сжатия, которые мы встречаем, конечно, gzip редко используется в Windows. tar — это формат архива, который по умолчанию не сжимается, его необходимо комбинировать с gzip, чтобы сжать окончательный файл tar в файл tar.gz в формате gzip, который обычно обозначается аббревиатурой tgz.

Почему Рар не упомянул? Поскольку это запатентованный алгоритм, вы можете получить инструменты для замораживания бесплатно, а инструменты для сжатия — за отдельную плату. Поэтому мы обычно используем сценарий, редко предоставляя сжатые файлы RAR.

В этой статье будет показано, как сжатие и распаковка gzip, tar, tgz и zip реализованы в Node.js соответственно.

библиотека несжатых файлов

Библиотека несжатых файлов, используемая в этой статье, взята изurllib, вам нужно сначала клонировать его в указанный каталог.

git clone https://github.com/node-modules/urllib.git nodejs-compressing-demo

gzip

В мире Linux обязанности каждого инструмента будут очень чистыми и очень единственными, такими как gzip, который будет только сжимать файлы.Что касается того, как папка упакована и сжата, это не имеет к этому никакого отношения, это то, что tar Ответственный за.

Командная строка gzip для сжатия файла

Например, мы будемnodejs-compressing-demo/lib/urllib.jsФайл сжат, и вы получаетеurllib.js.gzфайл, исходный файл будет удален.

$ ls -l nodejs-compressing-demo/lib/urllib.js
-rw-r--r--  1 a  a  31318 Feb 12 11:27 nodejs-compressing-demo/lib/urllib.js

$ gzip nodejs-compressing-demo/lib/urllib.js

$ ls -l nodejs-compressing-demo/lib/urllib.js.gz
-rw-r--r--  1 a  a  8909 Feb 12 11:27 nodejs-compressing-demo/lib/urllib.js.gz

# 还原压缩文件
$ gunzip nodejs-compressing-demo/lib/urllib.js.gz

Размер файла уменьшен с 31318 байт до 8909 байт, что более чем в 3,5 раза превышает эффект сжатия.

Вы также можете комбинировать метод трубыcatкоманда для сжатия и сохранения файла как произвольного файла:

$ ls -l nodejs-compressing-demo/README.md
-rw-r--r--  1 a  a  13747 Feb 12 11:27 nodejs-compressing-demo/README.md

$ cat nodejs-compressing-demo/README.md | gzip > README.md.gz

$ ls -l README.md.gz
-rw-r--r--  1 a  a  4903 Feb 12 11:50 README.md.gz

Node.js реализует gzip

Конечно, мы не будем реализовывать gzip-алгоритм и инструменты с нуля, в мире Node.js кто-то уже подготовил для вас эти базовые библиотеки, нам просто нужно использовать их «из коробки».
В этой статье будет использоватьсяcompressingМодуль, реализующий весь код сжатия и распаковки.

Почему выбирают сжатие? Поскольку он имеет достаточное качество кода и гарантии модульного тестирования, он активно поддерживается, API очень удобен, а также поддерживает потоковые интерфейсы.

Обещанный интерфейс

const compressing = require('compressing');

// 选择 gzip 格式,然后调用 compressFile 方法
compressing.gzip.compressFile('nodejs-compressing-demo/lib/urllib.js', 'nodejs-compressing-demo/lib/urllib.js.gz')
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

// 解压缩是反响过程,接口都统一为 uncompress
compressing.gzip.uncompress('nodejs-compressing-demo/lib/urllib.js.gz', 'nodejs-compressing-demo/lib/urllib.js2')
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

В сочетании с моделью программирования async/await код записывается как обычная асинхронная операция ввода-вывода.

const compressing = require('compressing');

async function main() {
  try {
    await compressing.gzip.compressFile('nodejs-compressing-demo/lib/urllib.js',
      'nodejs-compressing-demo/lib/urllib.js.gz');
    console.log('success');
  } catch (err) {
    console.error(err);
  }

  // 解压缩
  try {
    await compressing.gzip.uncompress('nodejs-compressing-demo/lib/urllib.js.gz',
      'nodejs-compressing-demo/lib/urllib.js2');
    console.log('success');
  } catch (err) {
    console.error(err);
  }
}

main();

Потоковый интерфейс

Важно отметить, что при программировании в потоковом режиме необходимо обрабатывать каждый потокerrorсобытие, иУничтожить все потоки вручную.

fs.createReadStream('nodejs-compressing-demo/lib/urllib.js')
  .on('error', handleError)
  .pipe(new compressing.gzip.FileStream()) // It's a transform stream
  .on('error', handleError)
  .pipe(fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js.gz2'))
  .on('error', handleError);

// 解压缩,就是 pipe 的方向倒转过来
fs.createReadStream('nodejs-compressing-demo/lib/urllib.js.gz2')
  .on('error', handleError)
  .pipe(new compressing.gzip.UncompressStream()) // It's a transform stream
  .on('error', handleError)
  .pipe(fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js3'))
  .on('error', handleError);

По словам официальногоBackpressuring in Streamsрекомендуется, мы должны использоватьpumpмодули для работы со Stream Режим программирования, очистка этих потоков осуществляется помпой.

const pump = require('pump');

const source = fs.createReadStream('nodejs-compressing-demo/lib/urllib.js');
const target = fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js.gz2');

pump(source, new compressing.gzip.FileStream(), target, err => {
  if (err) {
    console.error(err);
  } else {
    console.log('success');
  }
});

// 解压缩
pump(fs.createReadStream('nodejs-compressing-demo/lib/urllib.js.gz2'),
    new compressing.gzip.FileStream(),
    fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js3'),
    err => {
  if (err) {
    console.error(err);
  } else {
    console.log('success');
  }
});

Интерфейс Peam Advantage

Интерфейс Stream выглядит сложнее, чем интерфейс Promise, почему такой сценарий применения? На самом деле, в сфере HTTP-сервисов модель Stream будет иметь большие преимущества, потому что HTTP-запрос сам по себе является Request Stream, если вы хотите вернуть загруженный файл со сжатием gzip, используя интерфейс Stream, не нужно сохранять загруженный файл на локальный диск, но использовать этот файловый поток напрямую.

использоватьПример кода для загрузки файла яйца, с небольшой модификацией мы можем добиться сжатия и возврата gzip.

const pump = require('pump');

class UploadFormController extends Controller {
  // ... other codes

  async upload() {
    const stream = await this.ctx.getFileStream();
    // 直接将压缩流赋值给 ctx.body,实现边压缩边返回的流式响应
    this.ctx.body = pump(stream, new compressing.gzip.FileStream());
  }
}

tar | gzip > tgz

Глава gzip может быть известна заранее, за упаковку папки отвечает tar 📦.
Например, чтобы nodejs-compressing-demo Упаковать всю папку в один файл и отправить его другим, что можно сделать с помощью команды tar.

$ tar -c -f nodejs-compressing-demo.tar nodejs-compressing-demo/

$ ls -l nodejs-compressing-demo.tar
-rw-r--r--  1 a  a  206336 Feb 12 14:01 nodejs-compressing-demo.tar

Как вы можете видеть, файл пакета TAR из общего количества большего, поскольку оно несжато, общий размер папки с фактическим рядом. Таким образом, мы будем сжиматься одновременно упаковкой.

$ tar -c -z -f nodejs-compressing-demo.tgz nodejs-compressing-demo/

$ ls -l nodejs-compressing-demo.tgz
-rw-r--r--  1 a  a  39808 Feb 12 14:07 nodejs-compressing-demo.tgz

Разница в размере между tar и tgz более чем в 5 раз, что может сильно уменьшить пропускную способность сети.

Node.js реализует tgz

Обещанный интерфейс

использовать сначалаcompressing.tar.compressDir(sourceDir, targetFile)Упакуйте папку в tar-файл, затем сожмите tar-файл в tgz-файл, используя описанный выше метод сжатия gzip.

const compressing = require('compressing');

compressing.tar.compressDir('nodejs-compressing-demo', 'nodejs-compressing-demo.tar')
  .then(() => {
    return compressing.gzip.compressFile('nodejs-compressing-demo.tar',
      'nodejs-compressing-demo.tgz');
  });
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

// 解压缩
compressing.gzip.uncompress('nodejs-compressing-demo.tgz', 'nodejs-compressing-demo.tar')
  .then(() => {
    return compressing.tar.uncompress('nodejs-compressing-demo.tar',
      'nodejs-compressing-demo2');
  });
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

В сочетании с моделью программирования async/await код становится легче читать:

const compressing = require('compressing');

async function main() {
  try {
    await compressing.tar.compressDir('nodejs-compressing-demo',
      'nodejs-compressing-demo.tar');
    await compressing.gzip.compressFile('nodejs-compressing-demo.tar',
      'nodejs-compressing-demo.tgz');
    console.log('success');
  } catch (err) {
    console.error(err);
  }
  
  // 解压缩
  try {
    await compressing.gzip.uncompress('nodejs-compressing-demo.tgz',
      'nodejs-compressing-demo.tar');
    await compressing.tar.uncompress('nodejs-compressing-demo.tar',
      'nodejs-compressing-demo2');
    console.log('success');
  } catch (err) {
    console.error(err);
  }
}

main();

Потоковый интерфейс

пройти черезcompressing.tar.Streamкласс, который может динамически добавлять произвольные файлы и папки в объект потока tar, что очень гибко.

const tarStream = new compressing.tar.Stream();
// dir
tarStream.addEntry('dir/path/to/compress');
// file
tarStream.addEntry('file/path/to/compress');
// buffer
tarStream.addEntry(buffer);
// stream
tarStream.addEntry(stream);

const destStream = fs.createWriteStream('path/to/destination.tgz');
pump(tarStream, new compressing.gzip.FileStream(), destStream, err => {
  if (err) {
    console.error(err);
  } else {
    console.log('success');
  }
});

zip

zip на самом деле можно рассматривать как "коммерческую" комбинацию tar + gzip. Это позволяет пользователям не различать сжатые файлы и сжатые папки. Во всяком случае, я использую zip.

Пример архивирования папки с помощью инструмента командной строки zip:

$ zip -r nodejs-compressing-demo.zip nodejs-compressing-demo/
  adding: nodejs-compressing-demo/ (stored 0%)
  adding: nodejs-compressing-demo/test/ (stored 0%)
  ...
  adding: nodejs-compressing-demo/.travis.yml (deflated 36%)

$ ls -l nodejs-compressing-demo.*
-rw-r--r--  1 a  a  206336 Feb 12 14:06 nodejs-compressing-demo.tar
-rw-r--r--  1 a  a   39808 Feb 12 14:07 nodejs-compressing-demo.tgz
-rw-r--r--  1 a  a   55484 Feb 12 14:34 nodejs-compressing-demo.zip

Сравнивая размер файлов tgz и zip, можно увидеть, что при параметрах сжатия по умолчанию эффект от gzip будет лучше, чем от zip.

Node.js реализует zip

Код реализации аналогичен tar, за исключением того, что по умолчанию он сжат, и нет необходимости добавлять процесс gzip.

const compressing = require('compressing');

compressing.zip.compressDir('nodejs-compressing-demo', 'nodejs-compressing-demo.zip')
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

// 解压缩
compressing.zip.uncompress('nodejs-compressing-demo.zip', 'nodejs-compressing-demo3')
  .then(() => {
    console.log('success');
  })
  .catch(err => {
    console.error(err);
  });

Суммировать

Сжатие и распаковка на основе Node.js проще, чем вы думаете? Благодаря гигантскому npm программирование может быть таким же простым, как инструмент командной строки. Будь то интерфейс Promise или интерфейс Stream, он имеет наиболее подходящий сценарий, выберете ли вы его?

На данный момент, с имеющимися у вас возможностями сжатия и распаковки, какие услуги и функции вы можете выполнять?

Надеюсь, эта статья будет вам полезна ❤️