Sie müssen ein gesamtes Verzeichnis mit Node.js komprimieren

107

Ich muss ein ganzes Verzeichnis mit Node.js komprimieren. Ich verwende derzeit Node-Zip und jedes Mal, wenn der Prozess ausgeführt wird, wird eine ungültige ZIP-Datei generiert (wie Sie aus diesem Github-Problem ersehen können ).

Gibt es eine andere, bessere Node.js-Option, mit der ich ein Verzeichnis komprimieren kann?

EDIT: Am Ende habe ich Archiver verwendet

writeZip = function(dir,name) {
var zip = new JSZip(),
    code = zip.folder(dir),
    output = zip.generate(),
    filename = ['jsd-',name,'.zip'].join('');

fs.writeFileSync(baseDir + filename, output);
console.log('creating ' + filename);
};

Beispielwert für Parameter:

dir = /tmp/jsd-<randomstring>/
name = <randomstring>

AKTUALISIEREN: Für diejenigen, die nach der von mir verwendeten Implementierung fragen, ist hier ein Link zu meinem Downloader :

commadelimited
quelle
3
Jemand auf Twitter schlug die child_process-API vor und rief einfach die System-ZIP auf: nodejs.org/api/child_process.html
commadelimited
1
Ich habe den child_process-Ansatz ausprobiert. Es hat zwei Einschränkungen. 1) Der Unix- zipBefehl enthält die gesamte übergeordnete Ordnerhierarchie des aktuellen Arbeitsverzeichnisses in der komprimierten Datei. Das könnte für dich in Ordnung sein, es war nicht für mich. Auch das Ändern des aktuellen Arbeitsverzeichnisses in child_process wirkt sich irgendwie nicht auf die Ergebnisse aus. 2) Um dieses Problem zu lösen, müssen Sie pushdin den Ordner springen, den Sie komprimieren möchten, und zip -rda er pushd in bash und nicht in / bin / sh integriert ist, müssen Sie auch / bin / bash verwenden. In meinem speziellen Fall war dies nicht möglich. Nur ein Kopf hoch.
Johnozbay
2
Mit der child_process.execAPI von @johnozbay node können Sie den cwd angeben, von dem aus Sie den Befehl ausführen möchten. Durch Ändern des CWD wird das Problem der übergeordneten Ordnerhierarchie behoben. Es behebt auch das Problem, dass es nicht benötigt wird pushd. Ich kann child_process nur empfehlen.
Govind Rai
1
stackoverflow.com/a/49970368/2757916 native nodejs-Lösung mit der API child_process. 2 Codezeilen. Keine Bibliotheken von Drittanbietern.
Govind Rai
@ GovindRai Vielen Dank!
Johnozbay

Antworten:

123

Am Ende habe ich archiver lib verwendet. Funktioniert super.

Beispiel

var file_system = require('fs');
var archiver = require('archiver');

var output = file_system.createWriteStream('target.zip');
var archive = archiver('zip');

output.on('close', function () {
    console.log(archive.pointer() + ' total bytes');
    console.log('archiver has been finalized and the output file descriptor has closed.');
});

archive.on('error', function(err){
    throw err;
});

archive.pipe(output);

// append files from a sub-directory and naming it `new-subdir` within the archive (see docs for more options):
archive.directory(source_dir, false);
archive.finalize();
commadelimited
quelle
1
Es scheint keine Beispiele dafür zu geben. Stört es Sie, zu teilen, was Sie getan haben?
Sinetheta
1
Der Archivierer unterstützt derzeit leider keine Unicode-Zeichen in Dateinamen. Berichtet an github.com/ctalkington/node-archiver/issues/90 .
Auge
2
Wie füge ich alle Dateien und Verzeichnisse rekursiv ein (auch die versteckten Dateien / Verzeichnisse)?
Ionică Bizău
12
Archiver macht dies jetzt noch einfacher. Anstatt die Methode mass () zu verwenden, können Sie jetzt directory () verwenden: mass npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman
14
.bulkist veraltet
chovy
45

Ich gebe nicht vor, etwas Neues zu zeigen, sondern möchte nur die obigen Lösungen für diejenigen zusammenfassen, die Promise-Funktionen in ihrem Code verwenden möchten (wie ich).

const archiver = require('archiver');

/**
 * @param {String} source
 * @param {String} out
 * @returns {Promise}
 */
function zipDirectory(source, out) {
  const archive = archiver('zip', { zlib: { level: 9 }});
  const stream = fs.createWriteStream(out);

  return new Promise((resolve, reject) => {
    archive
      .directory(source, false)
      .on('error', err => reject(err))
      .pipe(stream)
    ;

    stream.on('close', () => resolve());
    archive.finalize();
  });
}

Hoffe es wird jemandem helfen;)

D. Dimitrioglo
quelle
Was genau ist "draußen" hier? Ich nehme an, Quelle ist der Pfad des Verzeichnisses
Träume
@ Tarun voller Zip Pfad wie: /User/mypc/mydir/test.zip
D.Dimitrioglo
Die Zip-Datei kann nicht entpackt werden. Operation nicht erlaubt
Jake
@ ekaj_03 bitte stellen Sie sicher, dass Sie genügend Rechte für das angegebene Verzeichnis haben
D.Dimitrioglo
1
@ D.Dimitrioglo alles gut. Es war das Quellverzeichnis. Danke :)
Jake
17

Verwenden Sie dazu die native child_processAPI von Node .

Keine Notwendigkeit für Bibliotheken von Drittanbietern. Zwei Codezeilen.

const child_process = require("child_process");
child_process.execSync(`zip -r DESIRED_NAME_OF_ZIP_FILE_HERE *`, {
  cwd: PATH_TO_FOLDER_YOU_WANT_ZIPPED_HERE
});

Ich verwende die synchrone API. Sie können verwenden, child_process.exec(path, options, callback)wenn Sie Async benötigen. Es gibt viel mehr Optionen als nur die Angabe des CWD, um Ihre Anforderungen weiter zu optimieren. Siehe exec / execSync- Dokumente.


Bitte beachten Sie: In diesem Beispiel wird davon ausgegangen, dass Sie das Dienstprogramm zip auf Ihrem System installiert haben (zumindest mit OSX). Auf einigen Betriebssystemen ist möglicherweise kein Dienstprogramm installiert (dh die AWS Lambda-Laufzeit funktioniert nicht). In diesem Fall können Sie die Binärdatei des Zip-Dienstprogramms hier problemlos abrufen und zusammen mit Ihrem Anwendungsquellcode verpacken (für AWS Lambda können Sie sie auch in einer Lambda-Schicht verpacken), oder Sie müssen entweder ein Modul eines Drittanbieters verwenden (von denen es viele auf NPM gibt). Ich bevorzuge den früheren Ansatz, da sich das ZIP-Dienstprogramm seit Jahrzehnten bewährt hat.

Govind Rai
quelle
9
Funktioniert leider nur auf Systemen, die haben zip.
Janpio
3
Ich habe
es macht Sinn, aber wenn ich nicht falsch bin, schraubt dies Windows-Benutzer wieder über. Bitte denken Sie an die Windows-Benutzer!
Mathijs Segers
@ MathijsSegers haha! Deshalb habe ich einen Link zur Binärdatei eingefügt, damit Windows-Benutzer ihn auch erhalten können! :)
Govind Rai
Gibt es eine Möglichkeit, dies für ein Verzeichnis innerhalb eines Projekts und nicht für ein Computerverzeichnis zum Laufen zu bringen?
Matt Croak
13

Archive.bulkist jetzt veraltet, die neue Methode, die dafür verwendet werden soll, ist glob :

var fileName =   'zipOutput.zip'
var fileOutput = fs.createWriteStream(fileName);

fileOutput.on('close', function () {
    console.log(archive.pointer() + ' total bytes');
    console.log('archiver has been finalized and the output file descriptor has closed.');
});

archive.pipe(fileOutput);
archive.glob("../dist/**/*"); //some glob pattern here
archive.glob("../dist/.htaccess"); //another glob pattern
// add as many as you like
archive.on('error', function(err){
    throw err;
});
archive.finalize();
caiocpricci2
quelle
2
Ich habe mich darüber gewundert, sie sagten, die Masse sei veraltet, schlugen aber nicht vor, welche Funktion stattdessen verwendet werden sollte.
Jarodsmk
1
Wie geben Sie das "Quell" -Verzeichnis an?
Träume
Versuchen Sie einmal den folgenden Ansatz: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari
2020: archive.directory () ist viel einfacher!
OhadR
9

So schließen Sie alle Dateien und Verzeichnisse ein:

archive.bulk([
  {
    expand: true,
    cwd: "temp/freewheel-bvi-120",
    src: ["**/*"],
    dot: true
  }
]);

Darunter wird Node-Glob ( https://github.com/isaacs/node-glob ) verwendet, sodass jeder damit kompatible übereinstimmende Ausdruck funktioniert.

Sam Ghaderyan
quelle
.bulk ist veraltet
Mohamad Hamouday
9

Dies ist eine weitere Bibliothek, die den Ordner in einer Zeile komprimiert : zip-local

var zipper = require('zip-local');

zipper.sync.zip("./hello/world/").compress().save("pack.zip");
Träume
quelle
4
Arbeitete wie ein Zauber, im Gegensatz zu Dutzenden anderen, die im Internet verfügbar sind oder oben erwähnt wurden und für mich immer eine Datei mit Null Bytes generierten
Sergey Pleshakov,
4

So leiten Sie das Ergebnis an das Antwortobjekt weiter (Szenarien, in denen die Zip-Datei heruntergeladen und nicht lokal gespeichert werden muss)

 archive.pipe(res);

Sams Hinweise für den Zugriff auf den Inhalt des Verzeichnisses haben bei mir funktioniert.

src: ["**/*"]
Raf
quelle
3

Adm-zip hat Probleme beim Komprimieren eines vorhandenen Archivs https://github.com/cthackers/adm-zip/issues/64 sowie Beschädigungen beim Komprimieren von Binärdateien.

Ich habe auch Probleme mit der Komprimierungsbeschädigung mit Node-Zip https://github.com/daraosn/node-zip/issues/4 festgestellt

Der Node-Archiver ist der einzige, der gut zu komprimieren scheint, aber keine unkomprimierten Funktionen hat.

Xiaoxin
quelle
1
Über welchen Node-Archiver sprechen Sie? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe
@firian Er hat nicht Archiver gesagt, er hat Adm-zip gesagt.
Francis Pelland
5
@FrancisPelland Umm, im letzten Satz schrieb er " Node-Archiver ist der einzige, der zu funktionieren scheint " - darauf beziehe ich mich.
Biphobe
Ich glaube, er hat npmjs.com/package/archiver
OhadR
2

Ich habe diese kleine Bibliothek gefunden, die zusammenfasst, was Sie brauchen.

npm install zip-a-folder

const zip-a-folder = require('zip-a-folder');
await zip-a-folder.zip('/path/to/the/folder', '/path/to/archive.zip');

https://www.npmjs.com/package/zip-a-folder

Ondrej Kvasnovsky
quelle
Ist es möglich, Parameter hinzuzufügen, um einen Zip-Ordner zu erstellen? wie komprimierte Ebene und Größe, wenn ja, wie geht das?
Trang D
1

Da archiveres lange Zeit nicht mit der neuen Version von Webpack kompatibel ist, empfehle ich die Verwendung von zip-lib .

var zl = require("zip-lib");

zl.archiveFolder("path/to/folder", "path/to/target.zip").then(function () {
    console.log("done");
}, function (err) {
    console.log(err);
});
tao
quelle
0

Sie können es auf einfache Weise versuchen:

Installieren zip-dir:

npm install zip-dir

und benutze es

var zipdir = require('zip-dir');

let foldername =  src_path.split('/').pop() 
    zipdir(<<src_path>>, { saveTo: 'demo.zip' }, function (err, buffer) {

    });
Harsha Biyani
quelle
Ist es möglich, Parameter hinzuzufügen, um einen Zip-Ordner zu erstellen? wie komprimierte Ebene und Größe, wenn ja, wie geht das?
Trang D
0

Am Ende habe ich den Archivierer umwickelt, um JSZip zu emulieren, da das Refactoring durch mein Projekt zu viel Aufwand erfordert. Ich verstehe, dass Archiver vielleicht nicht die beste Wahl ist, aber los geht's.

// USAGE:
const zip=JSZipStream.to(myFileLocation)
    .onDone(()=>{})
    .onError(()=>{});

zip.file('something.txt','My content');
zip.folder('myfolder').file('something-inFolder.txt','My content');
zip.finalize();

// NodeJS file content:
    var fs = require('fs');
    var path = require('path');
    var archiver = require('archiver');

  function zipper(archive, settings) {
    return {
        output: null,
        streamToFile(dir) {
            const output = fs.createWriteStream(dir);
            this.output = output;
            archive.pipe(output);

            return this;
        },
        file(location, content) {
            if (settings.location) {
                location = path.join(settings.location, location);
            }
            archive.append(content, { name: location });
            return this;
        },
        folder(location) {
            if (settings.location) {
                location = path.join(settings.location, location);
            }
            return zipper(archive, { location: location });
        },
        finalize() {
            archive.finalize();
            return this;
        },
        onDone(method) {
            this.output.on('close', method);
            return this;
        },
        onError(method) {
            this.output.on('error', method);
            return this;
        }
    };
}

exports.JSzipStream = {
    to(destination) {
        console.log('stream to',destination)
        const archive = archiver('zip', {
            zlib: { level: 9 } // Sets the compression level.
        });
        return zipper(archive, {}).streamToFile(destination);
    }
};
user672770
quelle