2016-07-25 12 views
2

Ich habe eine Gulp-Aufgabe, wo ich viele Dateien (mehr als 2700 in einem Fall, in einigen anderen Fällen mehrere tausend) in einer ZIP-Datei hinzufügen kann. Der Code ist wie folgt:Wie erstelle ich eine ZIP-Datei mit Gulp, die viele Dateien enthält?

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(); 
    })); 

Dieser Code funktioniert auf kleine Projekte, aber wenn es mit mehr als 2000 Dateien handelt, erhalte ich folgende Fehlermeldung:

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

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

So dass ich verstehe, mit 2000+ Dateien, die gleichzeitig geöffnet werden, bevor sie in den ZIP geschrieben werden, sind keine gute Idee.

Wie kann ich die ZIP-Datei schreiben, ohne dass ich alle Dateien öffnen muss?

Danke.

Informationen: Knoten 5.5.0/3.8.5 npm/archiver 1.0.0/windows

+1

'archiver.append' ein' callback' als letztes Argument akzeptiert, vielleicht versuchen, es den Stream zu schließen? – jjm

Antwort

2

Gulp nimmt bereits um eine Menge Dinge, die Sie zu tun versuchen:

  • gulp.src() liest den Dateiinhalt und macht einen fs.stat() Aufruf für jede Datei. Es speichert dann sowohl file.contents als auch file.stat auf den vinyl-file Objekten, die es aussendet.
  • Dies geschieht durch Verwendung des graceful-fs-Pakets, das im Falle eines EMFILE-Fehlers automatisch zurückgesetzt wird und erneut versucht, wenn eine andere Datei geschlossen wird. Das verhindert das "zu viele offene Dateien" Problem, das Sie erleben.

Leider sind Sie nicht nutzen, alle diejenigen zu nehmen, weil:

  • Sie explizite Aufrufe an fs.statSync() und fs.createReadStream() machen. Das ist wirklich nicht nötig, da der Schluck das schon für dich getan hat. Sie lesen effektiv jede Datei zweimal (und erstellen dabei doppelt so viele Dateideskriptoren).
  • Sie umgehen den integrierten Schutz von Gulp gegen EMFILE, indem Sie direkt das Modul fs verwenden, das keine Schutzvorrichtungen gegen das Problem "zu viele offene Dateien" enthält.

Ich habe Ihren Code umgeschrieben, um die Funktionen von gulp zu nutzen. Ich habe auch versucht, es etwas schluckbarer zu machen, z. von gulp-filter mit loszuwerden, die Verzeichnisse zu bekommen:

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(); 
    })); 
}); 
+0

Vielen Dank, nicht nur für den richtigen Code, sondern auch für Ihre vollständige Erklärung! – romaintaz