2

ZIP ファイルに大量のファイル (ある場合は 2700 以上、場合によっては数千になることもあります) を追加する Gulp タスクがあります。コードは次のとおりです。

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)

したがって、ZIPに書き込む前に2000以上のファイルを同時に開くことはお勧めできません。

すべてのファイルを開かずに ZIP ファイルを書き込むようにするにはどうすればよいですか?

ありがとう。

情報: node 5.5.0 / npm 3.8.5 / archiver 1.0.0 / windows

4

1 に答える 1

2

Gulp は、あなたがやろうとしている多くのことを既に処理しています:

  • gulp.src()ファイルの内容を読み取りfs.stat()、各ファイルを呼び出します。次に、放出するオブジェクトとしてfile.contents、および放出するオブジェクトの両方に格納します。file.statvinyl-file
  • パッケージを使用してこれを行いgraceful-fsます。これは、EMFILE エラーの場合に自動的にバックオフし、別のファイルが閉じられたときに再試行します。これにより、発生している「開いているファイルが多すぎる」問題を防ぐことができます。

残念ながら、次の理由により、これらのいずれも利用していません。

  • fs.statSync()とを明示的に呼び出していますfs.createReadStream()。gulp が既にそれを行っているので、実際にはその必要はありません。各ファイルを効果的に 2 回読み取ります (そして、プロセスで 2 倍の数のファイル記述子を作成します)。
  • fs「開いているファイルが多すぎる」問題に対するガードを持たないモジュールを直接使用することにより、EMFILEに対するgulpの組み込み保護を回避しています。

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();
    }));
});
于 2016-07-25T19:54:10.040 に答える