Musisz spakować cały katalog za pomocą Node.js.

107

Muszę spakować cały katalog za pomocą Node.js. Obecnie używam node-zip i za każdym razem, gdy proces jest uruchamiany, generuje nieprawidłowy plik ZIP (jak widać z tego problemu na Github ).

Czy jest inna, lepsza opcja Node.js, która pozwoli mi spakować katalog?

EDYCJA: Skończyło się na używaniu archiwizatora

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

przykładowa wartość parametrów:

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

AKTUALIZACJA: Dla tych, którzy pytają o implementację, której użyłem, oto link do mojego downloadera :

Commadelimited
źródło
3
Ktoś na Twitterze zasugerował API child_process i po prostu zadzwoń do systemu ZIP: nodejs.org/api/child_process.html
commadelimited
1
Wypróbowałem podejście child_process. Ma dwa zastrzeżenia. 1)zip polecenie unix zawiera całą hierarchię folderów nadrzędnych bieżącego katalogu roboczego w spakowanym pliku. To może być dla ciebie w porządku, to nie dla mnie. Również zmiana bieżącego katalogu roboczego w child_process w jakiś sposób nie wpływa na wyniki. 2) Aby rozwiązać ten problem, musisz użyć, pushdaby wskoczyć do folderu, który chcesz spakować zip -r, ale ponieważ pushd jest wbudowany w bash, a nie / bin / sh, musisz również użyć / bin / bash. W moim przypadku nie było to możliwe. Tylko jedno ostrzeżenie.
johnozbay
2
Interfejs child_process.execAPI węzła @johnozbay pozwala określić cwd, z którego chcesz uruchomić polecenie. Zmiana CWD rozwiązuje problem hierarchii folderów nadrzędnych. Rozwiązuje również problem braku potrzeby pushd. W pełni polecam child_process.
Govind Rai
1
stackoverflow.com/a/49970368/2757916 natywne rozwiązanie nodejs korzystające z interfejsu API child_process. 2 linie kodu. Brak bibliotek innych firm.
Govind Rai
@GovindRai Wielkie dzięki!
johnozbay

Odpowiedzi:

124

Skończyło się na używaniu archiwizatora lib. Działa świetnie.

Przykład

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
źródło
1
Wydaje się, że nie ma żadnych przykładów, jak to zrobić, czy możesz podzielić się tym, co zrobiłeś?
Sinetheta
1
Niestety, archiwizator nie obsługuje obecnie znaków Unicode w nazwach plików. Zgłoszone na github.com/ctalkington/node-archiver/issues/90 .
Oko
2
Jak rekursywnie dołączyć wszystkie pliki i katalogi (także ukryte pliki / katalogi)?
Ionică Bizău
12
Archiver sprawia, że ​​teraz jest to jeszcze prostsze. Zamiast używać metody bulk (), możesz teraz użyć directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman
14
.bulkjest przestarzały
chovy
46

Nie udaję, że pokazuję coś nowego, po prostu chcę podsumować powyższe rozwiązania dla tych, którzy lubią używać funkcji Promise w swoim kodzie (jak ja).

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

Mam nadzieję, że to komuś pomoże;)

D.Dimitrioglo
źródło
co dokładnie jest „na zewnątrz”? zakładam, że źródłem jest ścieżka do katalogu
Dreams
@Tarun pełna ścieżka zip, taka jak: /User/mypc/mydir/test.zip
D.Dimitrioglo
Nie można rozpakować pliku zip. Operacja niedozwolona
Jake
@ ekaj_03, upewnij się, że masz wystarczające prawa do określonego katalogu
D.Dimitrioglo
1
@ D.Dimitrioglo wszystko dobrze. To był numer źródłowy w reż. Dzięki :)
Jake
17

child_processAby to osiągnąć, użyj natywnego interfejsu API Node .

Nie ma potrzeby korzystania z bibliotek innych firm. Dwie linie kodu.

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

Używam synchronicznego interfejsu API. Możesz użyć, child_process.exec(path, options, callback)jeśli potrzebujesz asynchronicznego. Istnieje znacznie więcej opcji niż tylko określenie CWD, aby jeszcze bardziej dopracować swoje żądania. Zobacz dokumentację exec / execSync .


Uwaga: w tym przykładzie założono, że w systemie jest zainstalowane narzędzie zip (przynajmniej jest dostarczane z systemem OSX). Niektóre systemy operacyjne mogą nie mieć zainstalowanego narzędzia (np. Środowisko uruchomieniowe AWS Lambda nie). W takim przypadku możesz łatwo pobrać plik binarny narzędzia zip tutaj i spakować go wraz z kodem źródłowym aplikacji (w przypadku AWS Lambda możesz również spakować go w Lambda Layer) lub będziesz musiał użyć modułu innej firmy (których na NPM jest mnóstwo). Wolę to pierwsze podejście, ponieważ narzędzie ZIP jest wypróbowywane i testowane od dziesięcioleci.

Govind Rai
źródło
9
Niestety działa tylko w systemach, które mają zip.
janpio
3
Poszedłem na to rozwiązanie tylko po to, aby uniknąć dziesiątek bibliotek zewnętrznych w moim projekcie
EAzevedo
ma to sens, ale jeśli się nie mylę, to jest to ponowne dokuczanie użytkownikom Windowsa. Proszę pomyśleć o użytkownikach systemu Windows!
Mathijs Segers
@MathijsSegers haha! dlatego dołączyłem link do pliku binarnego, aby użytkownicy systemu Windows też mogli go zdobyć! :)
Govind Rai
Czy istnieje sposób, aby to działało w katalogu w projekcie, a nie w katalogu komputera?
Matt Croak
13

Archive.bulkjest teraz przestarzała, nową metodą, która ma być używana, jest 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
źródło
2
Zastanawiałem się nad tym, powiedzieli, że zbiorcze jest przestarzałe, ale nie sugerowali, której funkcji użyć zamiast tego.
jarodsmk
1
Jak określić katalog „źródłowy”?
Dreams
Wypróbuj raz poniższe podejście: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari
2020: archive.directory () jest znacznie prostsza!
OhadR
9

Aby uwzględnić wszystkie pliki i katalogi:

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

Używa pod spodem node-glob ( https://github.com/isaacs/node-glob ), więc każde pasujące wyrażenie będzie działać.

Sam Ghaderyan
źródło
.bulk jest przestarzały
Mohamad Hamouday
9

To jest kolejna biblioteka, która zamyka folder w jednym wierszu: zip-local

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

zipper.sync.zip("./hello/world/").compress().save("pack.zip");
Sny
źródło
4
Pracował jak czar, w przeciwieństwie do kilkunastu innych dostępnych w internecie lub wspomniano powyżej, który zawsze wygenerowany plik „zero bajtów” dla mnie
Sergey Pleshakov
4

Aby przesłać wynik do obiektu odpowiedzi (scenariusze, w których istnieje potrzeba pobrania pliku zip zamiast przechowywania lokalnie)

 archive.pipe(res);

Podpowiedzi Sama dotyczące dostępu do zawartości katalogu zadziałały w moim przypadku.

src: ["**/*"]
Raf
źródło
3

Adm-zip ma problemy z samą kompresją istniejącego archiwum https://github.com/cthackers/adm-zip/issues/64, a także z uszkodzeniem podczas kompresji plików binarnych.

Napotkałem również problemy z uszkodzeniem kompresji w przypadku node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver jest jedynym, który wydaje się dobrze działać w kompresji, ale nie ma żadnych funkcji dekompresowania.

Xiaoxin
źródło
1
O którym archiwizatorze węzłów mówisz? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe
@firian Nie powiedział Archiver, powiedział Adm-zip.
Francis Pelland
5
@FrancisPelland Umm, w ostatnim zdaniu napisał, że „ archiwizator węzłów jest jedynym, który wydaje się działać ” - o tym właśnie mówię.
biphobe
Myślę, że meatn npmjs.com/package/archiver
OhadR
2

Znalazłem tę małą bibliotekę, która zawiera to, czego potrzebujesz.

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
źródło
Czy można dodać parametry, aby utworzyć folder zip? jak skompresowany poziom i rozmiar, jeśli tak, jak to zrobić?
Trang D
1

Ponieważ archivernie jest kompatybilny z nową wersją webpacka przez długi czas, polecam użycie 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
źródło
0

Możesz spróbować w prosty sposób:

Zainstaluj zip-dir:

npm install zip-dir

i użyj go

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

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

    });
Harsha Biyani
źródło
czy można dodać parametry, aby utworzyć folder zip? jak skompresowany poziom i rozmiar, jeśli tak, jak to zrobić?
Trang D
0

Skończyło się na tym, że owijałem archiwizator, aby emulować JSZip, ponieważ refaktoryzacja mojego projektu wymagałaby zbyt dużego wysiłku. Rozumiem, że Archiver może nie być najlepszym wyborem, ale proszę bardzo.

// 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
źródło