Как создать ZIP-файл с Gulp, который содержит много файлов?

У меня есть задание Gulp, в котором я добавляю множество файлов (более 2700 в одном случае, но в некоторых других случаях это может быть несколько тысяч) в ZIP-файл. Код выглядит следующим образом:

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

let zip = fs.createWriteStream('my-archive.zip');
return gulp.src('app/**/*')
  .pipe(through.obj((file, encoding, cb) => {
    let pathInZip = '...';
    if (!isADirectory(file.path)) { // Do not zip the directory itself
      archiver.append(fs.createReadStream(file.path), {
        name: pathInZip,
        mode: fs.statSync(file.path)
      });
    }
    cb(null, file);
  }, cb => {
    // Now create the ZIP file!
    archiver.pipe(zip);
    archiver.finalize();
    cb();
  }));

Этот код работает на небольших проектах, но когда он имеет дело с более чем 2000 файлами, я получаю следующую ошибку:

events.js:154
throw er; // Unhandled 'error' event
^

Error: EMFILE: too many open files, open 'd:\dev\app\some\file'
at Error (native)

Поэтому я понимаю, что одновременное открытие более 2000 файлов перед записью их в ZIP-файл не очень хорошая идея.

Как я могу попросить записать ZIP-файл без необходимости открывать все файлы?

Благодарю.

Для информации: узел 5.5.0 / npm 3.8.5 / архиватор 1.0.0 / windows

1 ответ

Решение

Gulp уже заботится о многих вещах, которые вы пытаетесь сделать:

  • gulp.src() читает содержимое файла и делает fs.stat() вызов для каждого файла. Затем он хранит как file.contentsа также file.stat на vinyl-file объекты, которые он излучает.
  • Это делается с помощью graceful-fs пакет, который автоматически отключается в случае ошибки EMFILE и повторяется при закрытии другого файла. Это предотвращает проблему "слишком много открытых файлов".

К сожалению, вы не пользуетесь ни одним из них, потому что:

  • Вы делаете явные звонки fs.statSync() а также fs.createReadStream(), В этом действительно нет необходимости, поскольку gulp уже сделал это для вас. Вы фактически читаете каждый файл дважды (и создаете вдвое больше файловых дескрипторов в процессе).
  • Вы обходите встроенную защиту Gulp от EMFILE, используя прямое fs модуль, который не защищает от проблемы "слишком много открытых файлов".

Я переписал ваш код, чтобы воспользоваться возможностями gulp. Я также попытался сделать это немного более идиотским, например, используя gulp-filter избавиться от каталогов:

const gulp = require('gulp');
const fs = require('graceful-fs');
const archiver = require('archiver')('zip');
const through = require('through2');
const filter = require('gulp-filter');

gulp.task('default', () => {
  var zip = fs.createWriteStream('my-archive.zip');
  archiver.pipe(zip);
  return gulp.src('app/**/*')
    .pipe(filter((file) => !file.stat.isDirectory()))
    .pipe(through.obj((file, encoding, cb) => {
      var pathInZip = '...';
      archiver.append(file.contents, {
        name: pathInZip,
        mode: file.stat
      });
      cb(null, file);
    }, cb => {
      zip.on('finish', cb);
      archiver.finalize();
    }));
});
Другие вопросы по тегам