Einfachste Möglichkeit zum Herunterladen und Entpacken von Dateien in Node.js plattformübergreifendem?

73

Nur auf der Suche nach einer einfachen Lösung , um das Herunterladen und Entpacken .zipoder .tar.gzDateien in Node.js auf jedem Betriebssystem.

Ich bin mir nicht sicher, ob dies eingebaut ist oder ob ich eine separate Bibliothek verwenden muss. Irgendwelche Ideen? Suchen Sie nach nur ein paar Codezeilen. Wenn also die nächste Zip-Datei kommt, die ich in Node herunterladen möchte, ist dies ein Kinderspiel. Das Gefühl sollte einfach und / oder eingebaut sein, aber ich kann nichts finden. Vielen Dank!

Lance Pollard
quelle

Antworten:

37

Kasse adm-zip .

ADM-ZIP ist eine reine JavaScript-Implementierung für die Komprimierung von ZIP-Daten für NodeJS.

In der Bibliothek können Sie:

  • Dekomprimieren Sie Zip-Dateien direkt auf Festplatten- oder In-Memory-Puffer
  • Komprimieren Sie Dateien und speichern Sie sie im .zipFormat oder in komprimierten Puffern auf der Festplatte
  • Aktualisieren Sie den Inhalt von / fügen Sie neue hinzu / löschen Sie Dateien aus einer vorhandenen .zip
Bryanmac
quelle
1
Ich hatte Fehler mit jeder zlib-Implementierung, die einen Fehler "Ungültiger Blocktyp" (Fehlercode ist "Z_DATA_ERROR") auslöste, aber Windows Unzip würde für die Datei gut funktionieren. adm-zip scheint auch gut zu funktionieren.
GotDibbs
5
"Ungültiges oder nicht unterstütztes Zip-Format. Kein END-Header gefunden" mit Cygwin-ZIP-Datei. Bloargh.
PSP
Können wir unzipauch mit diesem Modul auf der Serverseite archivieren?
Manwal
Ich erhalte diesen Fehler mit dem Adm-Zip-Paket, das für mich nicht
funktioniert
1
Es kann keine große Datei entpacken. Zum Beispiel versucht , meine Datei mit einer Größe zu entpacken 2857964996throwRangeError [ERR_INVALID_OPT_VALUE]: The value "2857964996" is invalid for option "size"
Alexandre Annic
78

Es ist 2017 (26. Oktober, um genau zu sein).

Für eine alte und allgegenwärtige Technologie wie Unzip würde ich erwarten, dass es eine ziemlich beliebte, ausgereifte Unzip-Bibliothek von node.j gibt, die "stagniert" und "nicht gepflegt" ist, weil sie "vollständig" ist.

Die meisten Bibliotheken scheinen jedoch entweder völlig schrecklich zu sein oder erst vor wenigen Monaten Commits zu haben. Das ist ziemlich besorgniserregend ... also habe ich mehrere Entpackungsbibliotheken durchgesehen, ihre Dokumente gelesen und ihre Beispiele ausprobiert, um WTF herauszufinden. Zum Beispiel habe ich Folgendes versucht:

Top Empfehlung: yauzl

Funktioniert hervorragend für vollständig heruntergeladene Dateien. Nicht so gut zum Streamen.

Gut dokumentiert. Funktioniert gut. Macht Sinn.

2. Wahl: node-stream-zip

Antelles node-stream-zipscheint das Beste zu sein

Installieren:

npm install --save node-stream-zip

Verwendung:

'use strict';

var StreamZip = require('node-stream-zip');

var zip = new StreamZip({
  file: './example.zip'
, storeEntries: true
});

zip.on('error', function (err) { console.error('[ERROR]', err); });

zip.on('ready', function () {
  console.log('All entries read: ' + zip.entriesCount);
  //console.log(zip.entries());
});

zip.on('entry', function (entry) {
  var pathname = path.resolve('./temp', entry.name);
  if (/\.\./.test(path.relative('./temp', pathname))) {
      console.warn("[zip warn]: ignoring maliciously crafted paths in zip file:", entry.name);
      return;
  }

  if ('/' === entry.name[entry.name.length - 1]) {
    console.log('[DIR]', entry.name);
    return;
  }

  console.log('[FILE]', entry.name);
  zip.stream(entry.name, function (err, stream) {
    if (err) { console.error('Error:', err.toString()); return; }

    stream.on('error', function (err) { console.log('[ERROR]', err); return; });

    // example: print contents to screen
    //stream.pipe(process.stdout);

    // example: save contents to file
    mkdirp(path.dirname(pathname, function (err) {
      stream.pipe(fs.createWriteStream(pathname));
    });
  });
});

Sicherheitswarnung :

Ich bin mir nicht sicher, ob dies entry.namenach böswillig gestalteten Pfaden sucht, die falsch aufgelöst werden (z. B. ../../../foooder /etc/passwd).

Sie können dies leicht selbst überprüfen, indem Sie vergleichen /\.\./.test(path.relative('./to/dir', path.resolve('./to/dir', entry.name))).

Vorteile : (Warum denke ich, dass es das Beste ist?)

  • kann normale Dateien entpacken (vielleicht nicht einige verrückte mit seltsamen Erweiterungen)
  • kann streamen
  • scheint nicht die ganze Zip laden zu müssen, um Einträge zu lesen
  • hat Beispiele in normalem JavaScript (nicht kompiliert)
  • beinhaltet nicht die Küchenspüle (dh URL-Laden, S3 oder DB-Schichten)
  • verwendet vorhandenen Code aus einer beliebten Bibliothek
  • hat nicht zu viel sinnlosen Hipster oder Ninja-Foo im Code

Nachteile :

  • Schluckt Fehler wie ein hungriges Nilpferd
  • Wirft Zeichenfolgen anstelle von Fehlern (keine Stapelspuren)
  • zip.extract()scheint nicht zu funktionieren (daher habe ich zip.stream()in meinem Beispiel verwendet)

Zweiter Platz: Node-Unzipper

Installieren:

npm install --save unzipper

Verwendung:

'use strict';

var fs = require('fs');
var unzipper = require('unzipper');

fs.createReadStream('./example.zip')
  .pipe(unzipper.Parse())
  .on('entry', function (entry) {
    var fileName = entry.path;
    var type = entry.type; // 'Directory' or 'File'

    console.log();
    if (/\/$/.test(fileName)) {
      console.log('[DIR]', fileName, type);
      return;
    }

    console.log('[FILE]', fileName, type);

    // TODO: probably also needs the security check

    entry.pipe(process.stdout/*fs.createWriteStream('output/path')*/);
    // NOTE: To ignore use entry.autodrain() instead of entry.pipe()
  });

Vorteile :

  • Scheint ähnlich zu funktionieren node-stream-zip, aber weniger Kontrolle
  • Eine funktionellere Gabel von unzip
  • Scheint eher seriell als parallel zu laufen

Nachteile :

  • Küchenspüle viel? Enthält nur eine Menge Dinge, die nichts mit dem Entpacken zu tun haben
  • Liest die gesamte Datei (nach Chunk, was in Ordnung ist), nicht nur zufällige Suchvorgänge
coolaj86
quelle
46
Ist sonst niemand überrascht, dass es 2019 keine eingebaute Funktion gibt, mit der Sie eine .zipan einen bestimmten Ort extrahieren können ?
Felipe
1
Ich habe Adm-Zip und Yauzl / Yazl ausprobiert. yauzl / yazl unterstützen neuere Versionen des Zip-Protokolls und weniger Buggy.
Powpow
2
Möglicherweise möchten Sie hinzufügen, dass das Streamen einer Zip-Datei technisch eine ungültige Verwendung einer Zip-Datei ist. zip wurde in Tagen auf Diskette erstellt, daher besteht die Möglichkeit, neue Dateien zu einem vorhandenen zip aufzufrischen / hinzuzufügen. Angenommen, Ihre Zip hatte 3 Dateien A, B, C. Anstatt die gesamte Zip-Datei zu schreiben, fügt pkzip am Ende der Datei einfach ein neues A hinzu und fügt am Ende ein neues zentrales Verzeichnis ein. Wenn Sie also streamen, erhalten Sie 2 A-Dateien, von denen eine ungültig ist.
Gman
mkdirp ist undefiniert!
Ricardo G Saraiva
2
@Felipe Hurra! Fast 2021 und es ist immer noch schmerzhaft, mit Zip-Dateien im Knoten umzugehen!
Runsis
34

Node unterstützt gzip und deflate über das zlib-Modul :

var zlib = require('zlib');

zlib.gunzip(gzipBuffer, function(err, result) {
    if(err) return console.error(err);

    console.log(result);
});

Bearbeiten: Sie können pipedie Daten sogar direkt über zB Gunzip(auf Anfrage ):

var request = require('request'),
    zlib = require('zlib'),
    fs = require('fs'),
    out = fs.createWriteStream('out');

// Fetch http://example.com/foo.gz, gunzip it and store the results in 'out'
request('http://example.com/foo.gz').pipe(zlib.createGunzip()).pipe(out);

Für tar - Archive gibt es Isaacs' tar - Modul , das von NPM verwendet wird.

Bearbeiten 2: Die Antwort wurde aktualisiert, da zlibdas zipFormat nicht unterstützt wird . Dies funktioniert nur für gzip.

Linus Thiel
quelle
13
Keines dieser Beispiele funktioniert. Das zlib-Modul von node.js ist nur für Streams und Puffer vorgesehen, die einzelne Ressourcen darstellen. keine Zip- oder Teerarchive.
Pyrotechnik
3
Sie scheinen die Frage falsch zu verstehen. Er versucht nicht, einzelne Streams oder eine einzelne Datei zu dekomprimieren. Er versucht, die Dateien aus ganzen Archiven zu extrahieren. Wie Sie bereits erwähnt haben: Isaacs 'Teermodul funktioniert zwar mit Teeren, aber Ihr Code für Zips extrahiert die Dateien nicht aus Zip-Archiven.
Pyrotechnik
14

yauzl ist eine robuste Bibliothek zum Entpacken. Design-Prinzipien:

  • Folgen Sie der Spezifikation. Suchen Sie nicht nach lokalen Datei-Headern. Lesen Sie das zentrale Verzeichnis für Dateimetadaten.
  • Blockieren Sie den JavaScript-Thread nicht. Verwenden und Bereitstellen von asynchronen APIs.
  • Behalten Sie die Speichernutzung unter Kontrolle. Versuchen Sie nicht, ganze Dateien auf einmal im RAM zu puffern.
  • Niemals abstürzen (bei sachgemäßer Verwendung). Lassen Sie nicht zu, dass fehlerhafte Zip-Dateien Clientanwendungen zum Absturz bringen, die versuchen, Fehler abzufangen.
  • Fangen Sie unsichere Dateinameneinträge ab. Ein Zip-Dateieintrag gibt einen Fehler aus, wenn sein Dateiname mit "/" oder / [A-Za-z]: // beginnt oder wenn er ".." Pfadsegmente oder "\" enthält (gemäß Spezifikation).

Derzeit hat 97% Testabdeckung.

andrewrk
quelle
7
wurde mit einer noch einfacheren Schnittstelle
gepackt
12

Ich habe einige der Nodejs-Entpackungsbibliotheken ausprobiert, einschließlich adm-zip und unzip, und mich dann für extract-zip entschieden, einen Wrapper um yauzl. Schien am einfachsten zu implementieren.

https://www.npmjs.com/package/extract-zip

var extract = require('extract-zip')
extract(zipfile, { dir: outputPath }, function (err) {
   // handle err
})
Simon Hutchison
quelle
3
+1 Extrakt-Zip und Yauzl scheinen häufiger als andere beibehalten zu werden. Keine Verpflichtung zum Entpacken und Adm-Zip in 3 Jahren und Tonnen von Problemen zum Zeitpunkt des Schreibens!
Akseli Palén
4

Ich habe Erfolg mit folgendem gefunden, funktioniert mit .zip
(hier zum Posten vereinfacht: keine Fehlerprüfung & einfach alle Dateien in den aktuellen Ordner entpacken)

function DownloadAndUnzip(URL){
    var unzip = require('unzip');
    var http = require('http');
    var request = http.get(URL, function(response) {
        response.pipe(unzip.Extract({path:'./'}))
    });
}
Mtl Dev
quelle
3

Ich habe mich lange darauf gefreut und kein einfaches Arbeitsbeispiel gefunden, aber basierend auf diesen Antworten habe ich die downloadAndUnzip()Funktion erstellt.

Die Verwendung ist recht einfach:

downloadAndUnzip('http://your-domain.com/archive.zip', 'yourfile.xml')
    .then(function (data) {
        console.log(data); // unzipped content of yourfile.xml in root of archive.zip
    })
    .catch(function (err) {
        console.error(err);
    });

Und hier ist die Erklärung:

var AdmZip = require('adm-zip');
var request = require('request');

var downloadAndUnzip = function (url, fileName) {

    /**
     * Download a file
     * 
     * @param url
     */
    var download = function (url) {
        return new Promise(function (resolve, reject) {
            request({
                url: url,
                method: 'GET',
                encoding: null
            }, function (err, response, body) {
                if (err) {
                    return reject(err);
                }
                resolve(body);
            });
        });
    };

    /**
     * Unzip a Buffer
     * 
     * @param buffer
     * @returns {Promise}
     */
    var unzip = function (buffer) {
        return new Promise(function (resolve, reject) {

            var resolved = false;

            var zip = new AdmZip(buffer);
            var zipEntries = zip.getEntries(); // an array of ZipEntry records

            zipEntries.forEach(function (zipEntry) {
                if (zipEntry.entryName == fileName) {
                    resolved = true;
                    resolve(zipEntry.getData().toString('utf8'));
                }
            });

            if (!resolved) {
                reject(new Error('No file found in archive: ' + fileName));
            }
        });
    };


    return download(url)
        .then(unzip);
};
Adam
quelle
2
Dies ist nicht skalierbar, da es Speicher verwendet
Vanuan
Code könnte viel einfacher sein: const zipEntries = new AdmZip (Puffer) .getEntries () const output = zipEntries.filter (zipEntry => zipEntry.entryName == Dateiname) .map (zipEntry => zipEntry.getData (). ToString (' utf8 ')) return output.length> 0? Auflösen (Ausgabe): Ablehnen (neuer Fehler ('Keine Datei im Archiv gefunden:' + Dateiname))
sgracki
0

Ein weiteres Arbeitsbeispiel:

var zlib = require('zlib');
var tar = require('tar');
var ftp = require('ftp');

var files = [];

var conn = new ftp();
conn.on('connect', function(e) 
{
    conn.auth(function(e) 
    {
        if (e)
        {
            throw e;
        }
        conn.get('/tz/tzdata-latest.tar.gz', function(e, stream) 
        {
            stream.on('success', function() 
            {
                conn.end();

                console.log("Processing files ...");

                for (var name in files)
                {
                    var file = files[name];

                    console.log("filename: " + name);
                    console.log(file);
                }
                console.log("OK")
            });
            stream.on('error', function(e) 
            {
                console.log('ERROR during get(): ' + e);
                conn.end();
            });

            console.log("Reading ...");

            stream
            .pipe(zlib.createGunzip())
            .pipe(tar.Parse())
            .on("entry", function (e) 
            {    
                var filename = e.props["path"];
                console.log("filename:" + filename);
                if( files[filename] == null )
                {
                    files[filename] = "";
                }
                e.on("data", function (c) 
                {
                    files[filename] += c.toString();
                })    
            });
        });
    });
})
.connect(21, "ftp.iana.org");
Grundlegende Unterstützung für Grundelemente
quelle
2
gzip ist nicht dasselbe wie .zip
Benja
Beachten Sie, dass tar jetzt tar.Extract ([options]) mit dem Optionspfad '/ path' hat, der die untere Hälfte des Skripts speichert, siehe npmjs.org/package/tar
domenukk
0

Checkout Gunzip-Datei

import gunzip from 'gunzip-file';

const unzipAll = async () => {
  try {
    const compFiles = fs.readdirSync('tmp')
    await Promise.all(compFiles.map( async file => {
      if(file.endsWith(".gz")){
        gunzip(`tmp/${file}`, `tmp/${file.slice(0, -3)}`)
      }
    }));
  }
  catch(err) {
    console.log(err)
  }
}
arnaudjnn
quelle
-1

Herunterladen und extrahieren für .tar.gz:

const https = require("https");
const tar = require("tar");

https.get("https://url.to/your.tar.gz", function(response) {
  response.pipe(
    tar.x({
      strip: 1,
      C: "some-dir"
    })
  );
});
vitaliytv
quelle
-3

Sie können die vorhandenen Zip-Dateien auch einfach mit "entpacken" extrahieren. Es funktioniert für Dateien jeder Größe und Sie müssen es als Abhängigkeit von npm hinzufügen .

fs.createReadStream(filePath).pipe(unzip.Extract({path:moveIntoFolder})).on('close', function(){
        //To do after unzip
				callback();
		});

Sarath Kumar Rajendran
quelle
2
Bitte erläutern Sie, was unzipist und ob es sich um ein integriertes Paket handelt oder ob Sie es mit npm installieren müssen.
Jerinaw
1
Hier ist der Link für das Entpackungspaket. Es ist kein eingebautes Paket.
Sarath Kumar Rajendran