Perlu ZIP seluruh direktori menggunakan Node.js

107

Saya perlu membuat zip seluruh direktori menggunakan Node.js. Saat ini saya menggunakan node-zip dan setiap kali proses berjalan, itu menghasilkan file ZIP yang tidak valid (seperti yang Anda lihat dari masalah Github ini ).

Apakah ada opsi Node.js lain yang lebih baik yang memungkinkan saya untuk ZIP direktori?

EDIT: Saya akhirnya menggunakan Pengarsip

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

nilai sampel untuk parameter:

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

UPDATE: Bagi mereka yang bertanya tentang implementasi yang saya gunakan, berikut ini tautan ke pengunduh saya :

dibatasi
sumber
3
Seseorang di Twitter menyarankan API child_process, dan cukup panggil ZIP sistem: nodejs.org/api/child_process.html
commadelimited
1
Saya sudah mencoba pendekatan child_process. Ada dua peringatan. 1)zip perintah unix menyertakan semua hierarki folder induk dari direktori kerja saat ini dalam file zip. Ini mungkin baik untuk Anda, itu bukan untuk saya. Juga mengubah direktori kerja saat ini di child_process tidak mempengaruhi hasil. 2) Untuk mengatasi masalah ini, Anda harus menggunakan pushduntuk melompat ke folder yang akan Anda zip dan zip -r, tetapi karena pushd dibangun ke dalam bash dan bukan / bin / sh Anda perlu menggunakan / bin / bash juga. Dalam kasus khusus saya ini tidak mungkin. Hanya satu kepala.
johnozbay
2
Api node @johnozbay child_process.execmemungkinkan Anda menentukan cwd dari mana Anda ingin menjalankan perintah. Mengubah CWD memperbaiki masalah hierarki folder induk. Ini juga memperbaiki masalah tidak perlu pushd. Saya sangat merekomendasikan child_process.
Govind Rai
1
stackoverflow.com/a/49970368/2757916 solusi nodejs asli menggunakan api child_process. 2 baris kode. Tidak ada libs pihak ketiga.
Govind Rai
@GovindRai Banyak terima kasih!
johnozbay

Jawaban:

124

Saya akhirnya menggunakan lib pengarsipan . Bekerja dengan baik.

Contoh

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();
dibatasi
sumber
1
Sepertinya tidak ada contoh bagaimana melakukan ini, apakah Anda keberatan membagikan apa yang Anda lakukan?
Sinetheta
1
Pengarsip, sayangnya, tidak mendukung karakter Unicode dalam nama file seperti sekarang. Dilaporkan ke github.com/ctalkington/node-archiver/issues/90 .
Mata
2
Bagaimana cara memasukkan semua file dan direktori, secara rekursif (juga file / direktori tersembunyi)?
Ionică Bizău
12
Archiver membuat ini lebih sederhana sekarang. Daripada menggunakan metode bulk (), Anda sekarang dapat menggunakan directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman
14
.bulktidak digunakan lagi
chovy
46

Saya tidak berpura-pura menunjukkan sesuatu yang baru, hanya ingin merangkum solusi di atas untuk mereka yang suka menggunakan fungsi Promise dalam kodenya (seperti saya).

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

Semoga itu akan membantu seseorang;)

D. Dimitrioglo
sumber
apa sebenarnya yang "keluar" di sini? saya berasumsi sumber adalah jalur direktori
Dreams
@Tarun jalur zip lengkap seperti: /User/mypc/mydir/test.zip
D. Dimitrioglo
Tidak dapat mengekstrak file zip. Operasi tidak diizinkan
Jake
@ ekaj_03 pastikan bahwa Anda memiliki hak yang cukup untuk direktori tertentu
D. Dimitrioglo
1
@ D. Dimitrioglo semuanya bagus. Itu adalah masalah sumber dir. Terima kasih :)
Jake
17

Gunakan child_processapi asli Node untuk melakukannya.

Tidak perlu libs pihak ketiga. Dua baris kode.

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

Saya menggunakan API sinkron. Anda dapat menggunakan child_process.exec(path, options, callback)jika Anda membutuhkan async. Ada lebih banyak pilihan selain hanya menentukan CWD untuk lebih menyempurnakan permintaan Anda. Lihat dokumen exec / execSync .


Harap diperhatikan: Contoh ini mengasumsikan Anda telah menginstal utilitas zip di sistem Anda (setidaknya dilengkapi dengan OSX). Beberapa sistem operasi mungkin belum menginstal utilitas (yaitu, waktu proses AWS Lambda tidak). Dalam hal ini, Anda dapat dengan mudah mendapatkan biner utilitas zip di sini dan mengemasnya bersama dengan kode sumber aplikasi Anda (untuk AWS Lambda, Anda juga dapat mengemasnya dalam Lapisan Lambda), atau Anda harus menggunakan modul pihak ketiga (ada banyak tentang NPM). Saya lebih suka pendekatan sebelumnya, karena utilitas ZIP telah dicoba dan diuji selama beberapa dekade.

Govind Rai
sumber
9
Sayangnya hanya bekerja pada sistem yang memiliki zip.
janpio
3
Pergi untuk solusi ini hanya demi menghindari lusinan perpustakaan eksternal pada proyek saya
EAzevedo
masuk akal, tapi jika saya tidak salah ini mengacaukan pengguna windows lagi. Tolong pikirkan pengguna windows!
Mathijs Segers
@MathijsSegers haha! itulah mengapa saya menyertakan tautan ke biner sehingga pengguna windows bisa mendapatkannya juga! :)
Govind Rai
Adakah cara agar ini berfungsi untuk direktori dalam proyek daripada direktori komputer?
Matt Croak
13

Archive.bulksekarang tidak digunakan lagi, metode baru yang akan digunakan untuk ini adalah 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.dll
sumber
2
Ingin tahu tentang hal ini, mereka mengatakan massal sudah tidak digunakan lagi tetapi tidak menyarankan fungsi mana yang akan digunakan.
jarodsmk
1
Bagaimana Anda menentukan direktori "sumber"?
Mimpi
Coba sekali pendekatan di bawah ini: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari
2020: archive.directory () jauh lebih sederhana!
OhadR
9

Untuk memasukkan semua file dan direktori:

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

Ini menggunakan node-glob ( https://github.com/isaacs/node-glob ) di bawahnya, sehingga ekspresi apa pun yang cocok dengan itu akan berfungsi.

Sam Ghaderyan
sumber
.bulk tidak digunakan lagi
Mohamad Hamouday
9

Ini adalah pustaka lain yang mem-zip folder dalam satu baris: zip-local

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

zipper.sync.zip("./hello/world/").compress().save("pack.zip");
Mimpi
sumber
4
Bekerja seperti pesona, tidak seperti lusinan lainnya yang tersedia di internet atau disebutkan di atas, yang selalu menghasilkan file 'nol byte' untuk saya
Sergey Pleshakov
4

Untuk menyalurkan hasil ke objek respons (skenario di mana ada kebutuhan untuk mengunduh zip daripada menyimpan secara lokal)

 archive.pipe(res);

Petunjuk Sam untuk mengakses konten direktori berhasil untuk saya.

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

Adm-zip mengalami masalah hanya dengan mengompresi arsip yang ada https://github.com/cthackers/adm-zip/issues/64 serta kerusakan dengan mengompresi file biner.

Saya juga mengalami masalah korupsi kompresi dengan node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver adalah satu-satunya yang tampaknya berfungsi dengan baik untuk dikompresi tetapi tidak memiliki fungsionalitas yang tidak dikompresi.

Xiaoxin
sumber
1
Tentang pengarsip node mana yang Anda bicarakan? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe
@firian Dia tidak bilang Archiver, katanya Adm-zip.
Francis Pelland
5
@FrancisPelland Umm, di kalimat terakhir dia menulis " node-archiver adalah satu-satunya yang tampaknya berfungsi " - itulah yang saya maksud.
biphobe
Saya pikir dia makan npmjs.com/package/archiver
OhadR
2

Saya telah menemukan perpustakaan kecil ini yang merangkum apa yang Anda butuhkan.

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
sumber
Apakah mungkin menambahkan parameter untuk membuat folder zip? seperti tingkat dan ukuran terkompresi jika demikian, bagaimana cara melakukannya?
Trang D
1

Karena archivertidak kompatibel dengan versi baru webpack untuk waktu yang lama, saya sarankan menggunakan 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
sumber
0

Anda dapat mencoba dengan cara yang sederhana:

Pasang zip-dir:

npm install zip-dir

dan gunakan itu

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

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

    });
Harsha Biyani
sumber
apakah mungkin menambahkan parameter untuk membuat folder zip? seperti tingkat dan ukuran terkompresi jika demikian, bagaimana cara melakukannya?
Trang D
0

Saya akhirnya membungkus pengarsip untuk meniru JSZip, karena refactoring melalui proyek saya akan membutuhkan terlalu banyak usaha. Saya mengerti Archiver mungkin bukan pilihan terbaik, tapi ini dia.

// 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);
    }
};
pengguna672770
sumber