È necessario comprimere un'intera directory utilizzando Node.js


107

Ho bisogno di comprimere un'intera directory usando Node.js. Attualmente sto utilizzando node-zip e ogni volta che il processo viene eseguito genera un file ZIP non valido (come puoi vedere da questo problema di Github ).

C'è un'altra, migliore, opzione Node.js che mi consentirà di comprimere una directory?

EDIT: ho finito per usare l' archiver

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

valore di esempio per i parametri:

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

AGGIORNAMENTO: Per coloro che chiedono informazioni sull'implementazione che ho usato, ecco un collegamento al mio downloader :


3
Qualcuno su Twitter ha suggerito l'API child_process e chiama semplicemente il sistema ZIP: nodejs.org/api/child_process.html
commadelimited

1
Ho provato l'approccio child_process. Ha due avvertenze. 1) ilzip comando unix include tutta la gerarchia delle cartelle principali della directory di lavoro corrente nel file zippato. Potrebbe andar bene per te, non per me. Anche la modifica della directory di lavoro corrente in child_process in qualche modo non influisce sui risultati. 2) Per superare questo problema, devi usare pushdper saltare nella cartella che comprimerai e zip -r, ma poiché pushd è integrato in bash e non / bin / sh devi usare anche / bin / bash. Nel mio caso specifico questo non è stato possibile. Solo un avviso.
johnozbay

2
L' child_process.execAPI di @johnozbay node consente di specificare il cwd da cui si desidera eseguire il comando. La modifica del CWD risolve il problema della gerarchia delle cartelle principali. Risolve anche il problema di non aver bisogno pushd. Consiglio vivamente child_process.
Govind Rai

1
stackoverflow.com/a/49970368/2757916 nodejs nativi soluzione utilizzando API child_process. 2 righe di codice. Nessuna libreria di terze parti.
Govind Rai

@GovindRai Molte grazie!
johnozbay

Risposte:


124

Ho finito per usare l' archiver lib. Funziona alla grande.

Esempio

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

1
Non sembrano esserci esempi su come farlo, ti dispiace condividere quello che hai fatto?
Sinetheta

1
archiver, sfortunatamente, al momento non supporta i caratteri Unicode nei nomi dei file. Segnalato a github.com/ctalkington/node-archiver/issues/90 .
Occhio

2
Come posso includere tutti i file e le directory, in modo ricorsivo (anche i file / directory nascosti)?
Ionică Bizău

12
Archiver ora lo rende ancora più semplice. Invece di usare il metodo bulk (), ora puoi usare directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman

14
.bulkè deprecato
chovy

46

Non pretendo di mostrare qualcosa di nuovo, voglio solo riassumere le soluzioni sopra per coloro a cui piace usare le funzioni Promise nel proprio codice (come me).

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

Spero che possa aiutare qualcuno;)


che cosa è esattamente "fuori" qui? presumo che la fonte sia il percorso della directory
Dreams

@Tarun full zip's path like: /User/mypc/mydir/test.zip
D.Dimitrioglo

Impossibile decomprimere il file zip. Operazione non consentita
Jake

@ ekaj_03, assicurati di avere diritti sufficienti per la directory specificata
D.Dimitrioglo

1
@ D.Dimitrioglo tutto bene. Era il problema della directory di origine. Grazie :)
Jake

17

Usa l' child_processAPI nativa di Node per eseguire questa operazione.

Non c'è bisogno di librerie di terze parti. Due righe di codice.

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

Sto usando l'API sincrona. Puoi usare child_process.exec(path, options, callback)se hai bisogno di asincrono. Ci sono molte più opzioni oltre alla semplice specifica del CWD per ottimizzare ulteriormente le tue richieste. Vedere la documentazione di exec / execSync .


Nota: questo esempio presuppone che tu abbia l'utility zip installata sul tuo sistema (viene fornita con OSX, almeno). Alcuni sistemi operativi potrebbero non avere l'utilità installata (ad esempio, il runtime AWS Lambda no). In tal caso, puoi facilmente ottenere il binario dell'utilità zip qui e impacchettarlo insieme al codice sorgente dell'applicazione (per AWS Lambda puoi anche impacchettarlo in un livello Lambda), oppure dovrai utilizzare un modulo di terze parti (di cui ce ne sono molti su NPM). Preferisco il primo approccio, poiché l'utilità ZIP è provata e testata per decenni.


9
Purtroppo funziona solo su sistemi che dispongono di zip.
janpio

3
Sono andato a questa soluzione solo per evitare dozzine di librerie esterne sul mio progetto
EAzevedo

ha senso, ma se non sbaglio questo sta rovinando di nuovo gli utenti di Windows. Per favore pensa agli utenti di Windows!
Mathijs Segers

@MathijsSegers haha! ecco perché ho incluso un collegamento al binario in modo che anche gli utenti Windows possano ottenerlo! :)
Govind Rai

C'è un modo per farlo funzionare per una directory all'interno di un progetto piuttosto che per una directory del computer?
Matt Croak,

13

Archive.bulkè ora deprecato, il nuovo metodo da utilizzare per questo è 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();

2
Mi stavo chiedendo questo, hanno detto che il bulk era deprecato ma non hanno suggerito quale funzione utilizzare.
jarodsmk

1
Come si specifica la directory "sorgente"?
Dreams

Prova una volta l'approccio seguente: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari

2020: archive.directory () è molto più semplice!
OhadR


9

Questa è un'altra libreria che comprime la cartella in una riga: zip-local

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

zipper.sync.zip("./hello/world/").compress().save("pack.zip");

4
Ha funzionato come un incantesimo, a differenza di dozzine di altri disponibili su Internet o menzionati sopra, che hanno sempre generato file 'zero byte' per me
Sergey Pleshakov

4

Per reindirizzare il risultato all'oggetto risposta (scenari in cui è necessario scaricare lo zip anziché archiviarlo localmente)

 archive.pipe(res);

I suggerimenti di Sam per accedere al contenuto della directory hanno funzionato per me.

src: ["**/*"]

3

Adm-zip ha problemi a comprimere un archivio esistente https://github.com/cthackers/adm-zip/issues/64 e la corruzione con la compressione di file binari.

Ho anche riscontrato problemi di danneggiamento della compressione con node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver è l'unico che sembra funzionare bene per la compressione ma non ha alcuna funzionalità di decompressione.


1
Di quale nodo-archiviatore stai parlando? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe

@firian Non ha detto Archiver, ha detto Adm-zip.
Francis Pelland

5
@FrancisPelland Umm, nell'ultima frase ha scritto " node-archiver è l'unico che sembra funzionare " - questo è ciò a cui mi riferisco.
biphobe

penso che abbia pubblicato npmjs.com/package/archiver
OhadR

2

Ho trovato questa piccola libreria che racchiude ciò di cui hai bisogno.

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


È possibile aggiungere parametri per creare una cartella zip? come il livello e la dimensione compressi se sì, come farlo?
Trang D

1

Poiché archivernon è compatibile con la nuova versione di webpack da molto tempo, consiglio di utilizzare 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);
});

0

Puoi provare in modo semplice:

Installa zip-dir:

npm install zip-dir

e usalo

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

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

    });

è possibile aggiungere parametri per creare una cartella zip? come il livello e la dimensione compressi se sì, come farlo?
Trang D

0

Ho finito per avvolgere l'archiver per emulare JSZip, poiché il refactoring attraverso il mio progetto richiederebbe troppo impegno. Capisco che Archiver potrebbe non essere la scelta migliore, ma ecco qua.

// 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);
    }
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.