Besoin de compresser un répertoire entier en utilisant Node.js


109

J'ai besoin de compresser un répertoire entier en utilisant Node.js. J'utilise actuellement node-zip et chaque fois que le processus s'exécute, il génère un fichier ZIP non valide (comme vous pouvez le voir dans ce problème Github ).

Existe-t-il une autre option, meilleure, Node.js qui me permettra de compresser un répertoire?

EDIT: j'ai fini par utiliser l' archiveur

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

exemple de valeur pour les paramètres:

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

MISE À JOUR: Pour ceux qui posent des questions sur l'implémentation que j'ai utilisée, voici un lien vers mon téléchargeur :


3
Quelqu'un sur Twitter a suggéré l'API child_process, et appelle simplement le ZIP du système: nodejs.org/api/child_process.html
délimité

1
J'ai essayé l'approche child_process. Il y a deux mises en garde. 1) Lazip commande unix inclut toute la hiérarchie de dossiers parents du répertoire de travail actuel dans le fichier compressé. Cela pourrait vous convenir, ce n'était pas pour moi. La modification du répertoire de travail actuel dans child_process n'affecte pas non plus les résultats. 2) Pour surmonter ce problème, vous devez utiliser pushdpour sauter dans le dossier que vous allez compresser et zip -r, mais comme il pushd est intégré à bash et non à / bin / sh, vous devez également utiliser / bin / bash. Dans mon cas particulier, cela n'a pas été possible. Juste un avertissement.
johnozbay

2
L'API de @johnozbay node child_process.execvous permet de spécifier le cwd à partir duquel vous souhaitez exécuter la commande. La modification du CWD résout le problème de la hiérarchie des dossiers parents. Il résout également le problème de ne pas avoir besoin pushd. Je recommande pleinement child_process.
Govind Rai

1
stackoverflow.com/a/49970368/2757916 solution native nodejs utilisant l'api child_process. 2 lignes de code. Pas de bibliothèques tierces.
Govind Rai

@GovindRai Merci beaucoup!
johnozbay

Réponses:


128

J'ai fini par utiliser l' archiveur lib. Fonctionne très bien.

Exemple

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
Il ne semble pas y avoir d'exemples de la façon de faire cela, cela vous dérange-t-il de partager ce que vous avez fait?
Sinetheta

1
archiver, malheureusement, ne prend pas en charge les caractères Unicode dans les noms de fichiers pour le moment. Rapporté à github.com/ctalkington/node-archiver/issues/90 .
Eye

2
Comment inclure tous les fichiers et répertoires de manière récursive (également les fichiers / répertoires cachés)?
Ionică Bizău

12
Archiver rend cela encore plus simple maintenant. Plutôt que d'utiliser la méthode bulk (), vous pouvez maintenant utiliser directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman

14
.bulkest obsolète
chovy

49

Je ne prétends pas montrer quelque chose de nouveau, je veux juste résumer les solutions ci-dessus pour ceux qui aiment utiliser les fonctions Promise dans leur code (comme moi).

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

J'espère que cela aidera quelqu'un;)


qu'est-ce que "dehors" exactement ici? je suppose que la source est le chemin du répertoire
Dreams

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

Impossible de décompresser le fichier zip. Opération non autorisée
Jake

@ ekaj_03 veuillez vous assurer que vous avez suffisamment de droits pour le répertoire spécifié
D.Dimitrioglo

1
@ D.Dimitrioglo tout bon. C'était le problème du répertoire source. Merci :)
Jake

17

Utilisez l' child_processAPI native de Node pour ce faire.

Pas besoin de bibliothèques tierces. Deux lignes de code.

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

J'utilise l'API synchrone. Vous pouvez utiliser child_process.exec(path, options, callback)si vous avez besoin d'async. Il y a beaucoup plus d'options que de simplement spécifier le CWD pour affiner davantage vos demandes. Voir la documentation exec / execSync .


Remarque: cet exemple suppose que l'utilitaire zip est installé sur votre système (au moins, il est fourni avec OSX). Certains systèmes d'exploitation peuvent ne pas avoir d'utilitaire installé (c'est-à-dire que le runtime AWS Lambda n'en a pas). Dans ce cas, vous pouvez facilement obtenir le binaire de l'utilitaire zip ici et le conditionner avec le code source de votre application (pour AWS Lambda, vous pouvez également le conditionner dans une couche Lambda), ou vous devrez soit utiliser un module tiers (dont il y en a beaucoup sur NPM). Je préfère la première approche, car l'utilitaire ZIP est essayé et testé depuis des décennies.


10
Malheureusement, ne fonctionne que sur les systèmes qui ont zip.
janpio

3
Je suis allé chercher cette solution juste pour éviter des dizaines de bibliothèques externes sur mon projet
EAzevedo

cela a du sens, mais si je ne me trompe pas, cela vire à nouveau les utilisateurs de Windows. Pensez aux utilisateurs de Windows!
Mathijs Segers

@MathijsSegers haha! c'est pourquoi j'ai inclus un lien vers le binaire afin que les utilisateurs de Windows puissent l'obtenir aussi! :)
Govind Rai

Existe-t-il un moyen de faire fonctionner cela pour un répertoire dans un projet plutôt qu'un répertoire d'ordinateur?
Matt Croak

13

Archive.bulkest désormais obsolète, la nouvelle méthode à utiliser pour cela est 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
S'interroge à ce sujet, ils ont dit que le volume était obsolète mais n'ont pas suggéré quelle fonction utiliser à la place.
jarodsmk

1
Comment spécifiez-vous le répertoire "source"?
Dreams

Essayez une fois l'approche ci-dessous: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari

2020: archive.directory () est beaucoup plus simple!
OhadR


9

Ceci est une autre bibliothèque qui zippe le dossier en une seule ligne: zip-local

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

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

4
A fonctionné comme un charme, contrairement à des dizaines d'autres disponibles sur Internet ou mentionnés ci-dessus, qui ont toujours généré un fichier `` zéro octet '' pour moi
Sergey Pleshakov

4

Pour diriger le résultat vers l'objet de réponse (scénarios où il est nécessaire de télécharger le zip plutôt que de stocker localement)

 archive.pipe(res);

Les conseils de Sam pour accéder au contenu du répertoire ont fonctionné pour moi.

src: ["**/*"]

3

Adm-zip a des problèmes lors de la simple compression d'une archive existante https://github.com/cthackers/adm-zip/issues/64 ainsi que la corruption lors de la compression de fichiers binaires.

J'ai également rencontré des problèmes de corruption de compression avec node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver est le seul qui semble bien fonctionner pour la compression, mais il n'a aucune fonctionnalité de décompression.


1
De quel archiveur de nœuds parlez-vous? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe

@firian Il n'a pas dit Archiveur, il a dit Adm-zip.
Francis Pelland

5
@FrancisPelland Umm, dans la dernière phrase, il a écrit " node-archiver est le seul qui semble fonctionner " - c'est ce à quoi je fais référence.
biphobe

je pense qu'il vianden npmjs.com/package/archiver
OhadR

2

J'ai trouvé cette petite bibliothèque qui résume ce dont vous avez besoin.

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


Est-il possible d'ajouter des paramètres pour créer un dossier zip? comme le niveau compressé et la taille si oui, comment faire cela?
Trang D

1

Comme il archivern'est pas compatible avec la nouvelle version de webpack depuis longtemps, je recommande d'utiliser 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

Vous pouvez essayer de manière simple:

Installer zip-dir:

npm install zip-dir

et utilisez-le

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

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

    });

est-il possible d'ajouter des paramètres pour créer un dossier zip? comme le niveau compressé et la taille si oui, comment faire cela?
Trang D

0

J'ai fini par emballer l'archiveur pour émuler JSZip, car la refactorisation à travers mon projet ne demanderait pas trop d'efforts. Je comprends qu'Archiver n'est peut-être pas le meilleur choix, mais c'est parti.

// 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);
    }
};
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.