ต้องการ ZIP ทั้งไดเร็กทอรีโดยใช้ Node.js


115

ฉันต้องการซิปไดเร็กทอรีทั้งหมดโดยใช้ Node.js ฉันกำลังใช้ node-zip และทุกครั้งที่กระบวนการทำงานมันสร้างไฟล์ ZIP ที่ไม่ถูกต้อง (ดังที่คุณเห็นจากปัญหา Github นี้ )

มีตัวเลือก Node.js อื่นที่ดีกว่าที่จะช่วยให้ฉันสามารถ ZIP ขึ้นไดเร็กทอรีได้หรือไม่

แก้ไข: ฉันลงเอยด้วยการใช้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);
};

ค่าตัวอย่างสำหรับพารามิเตอร์:

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

UPDATE:สำหรับผู้ที่ถามเกี่ยวกับการใช้งานที่ฉันใช้นี่คือลิงค์ไปยังตัวดาวน์โหลดของฉัน :


3
มีคนบน Twitter แนะนำ child_process API และเรียกระบบ ZIP: nodejs.org/api/child_process.html
commadelimited

1
ฉันได้ลองใช้วิธี child_process แล้ว มันมีสองข้อแม้ 1)zipคำสั่งunix รวมลำดับชั้นของโฟลเดอร์หลักทั้งหมดของไดเร็กทอรีการทำงานปัจจุบันในไฟล์ซิป นี่อาจจะโอเคสำหรับคุณไม่ใช่สำหรับฉัน การเปลี่ยนไดเร็กทอรีการทำงานปัจจุบันใน child_process อย่างใดก็ไม่ส่งผลต่อผลลัพธ์ 2)เพื่อเอาชนะปัญหานี้คุณต้องใช้pushdเพื่อข้ามไปยังโฟลเดอร์ที่คุณจะ zip และzip -rแต่เนื่องจากpushd ถูกสร้างขึ้นใน bash และไม่ใช่ / bin / sh คุณต้องใช้ / bin / bash ด้วย ในกรณีเฉพาะของฉันสิ่งนี้ไม่สามารถทำได้ เพียงแค่หัวขึ้น
johnozbay

2
child_process.execapi ของโหนด @johnozbay ช่วยให้คุณระบุ cwd จากที่ที่คุณต้องการรันคำสั่ง การเปลี่ยน CWD จะช่วยแก้ปัญหาของลำดับชั้นของโฟลเดอร์หลัก pushdนอกจากนี้ยังช่วยแก้ไขปัญหาของการไม่ต้อง ฉันแนะนำ child_process อย่างเต็มที่
Govind Rai

1
stackoverflow.com/a/49970368/2757916โซลูชัน Native nodejs โดยใช้ child_process api รหัส 2 บรรทัด ไม่มีบุคคลที่สาม libs
Govind Rai

@GovindRai ขอบคุณมาก!
johnozbay

คำตอบ:


137

ฉันใช้ไฟล์archiver lib ใช้งานได้ดี

ตัวอย่าง

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, putting its contents at the root of archive
archive.directory(source_dir, false);

// append files from a sub-directory and naming it `new-subdir` within the archive
archive.directory('subdir/', 'new-subdir');

archive.finalize();

1
ดูเหมือนจะไม่มีตัวอย่างวิธีการทำเช่นนี้คุณสามารถแบ่งปันสิ่งที่คุณทำได้หรือไม่?
Sinetheta

1
ผู้จัดเก็บขออภัยไม่สนับสนุนอักขระ Unicode ในชื่อไฟล์ ณ ตอนนี้ รายงานไปยังgithub.com/ctalkington/node-archiver/issues/90
ตา

2
ฉันจะรวมไฟล์และไดเร็กทอรีทั้งหมดแบบวนซ้ำได้อย่างไร (รวมถึงไฟล์ / ไดเร็กทอรีที่ซ่อนอยู่)
IonicăBizău

12
Archiver ทำให้สิ่งนี้ง่ายยิ่งขึ้นในตอนนี้ แทนที่จะใช้เมธอด bulk () ตอนนี้คุณสามารถใช้ directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman

14
.bulkเลิกใช้แล้ว
chovy

55

ฉันไม่ได้แสร้งทำเป็นว่าจะแสดงสิ่งใหม่ ๆ เพียงแค่ต้องการสรุปวิธีแก้ปัญหาด้านบนสำหรับผู้ที่ชอบใช้ฟังก์ชัน Promise ในรหัสของพวกเขา (เช่นฉัน)

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

หวังว่ามันจะช่วยใครสักคน;)


"หมด" ตรงนี้คืออะไร? ฉันถือว่าแหล่งที่มาคือเส้นทางของไดเรกทอรี
ความฝัน

เส้นทาง @Tarun ซิปเต็มรูปแบบเช่น: /User/mypc/mydir/test.zip
D.Dimitrioglo

ไม่สามารถแตกไฟล์ zip ได้ ไม่อนุญาตให้ดำเนินการ
Jake

@ ekaj_03 โปรดตรวจสอบว่าคุณมีสิทธิ์เพียงพอสำหรับไดเรกทอรีที่ระบุ
D.Dimitrioglo

1
@ D.Dimitrioglo ดีทุกอย่าง มันเป็นปัญหาที่มาของ dir ขอบคุณ :)
Jake

19

ใช้child_processAPI ดั้งเดิมของ Node เพื่อทำสิ่งนี้ให้สำเร็จ

ไม่จำเป็นต้องมี libs ของบุคคลที่สาม รหัสสองบรรทัด

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

ฉันใช้ API แบบซิงโครนัส คุณสามารถใช้ได้child_process.exec(path, options, callback)ถ้าคุณต้องการ async มีตัวเลือกมากมายมากกว่าการระบุ CWD เพื่อปรับแต่งคำขอของคุณเพิ่มเติม ดูเอกสารexec / execSync


โปรดทราบ: ตัวอย่างนี้ถือว่าคุณได้ติดตั้งยูทิลิตี้ zip ไว้ในระบบของคุณ (อย่างน้อยก็มาพร้อมกับ OSX) ระบบปฏิบัติการบางระบบอาจไม่ได้ติดตั้งยูทิลิตี้ (เช่นรันไทม์ AWS Lambda ไม่ได้) ในกรณีนี้คุณสามารถรับไบนารียูทิลิตี้ zip ได้อย่างง่ายดายที่นี่และจัดแพคเกจพร้อมกับซอร์สโค้ดของแอปพลิเคชันของคุณ (สำหรับ AWS Lambda คุณสามารถจัดแพคเกจใน Lambda Layer ได้เช่นกัน) หรือคุณจะต้องใช้โมดูลของบุคคลที่สาม (ซึ่งมีมากมายใน NPM) ฉันชอบแนวทางเดิมเนื่องจากยูทิลิตี้ ZIP ถูกทดลองและทดสอบมานานหลายทศวรรษ


10
น่าเสียดายที่ใช้งานได้กับระบบที่มีzipไฟล์.
janpio

3
ไปหาวิธีแก้ปัญหานี้เพื่อหลีกเลี่ยงห้องสมุดภายนอกหลายสิบแห่งในโครงการของฉัน
EAzevedo

มันสมเหตุสมผล แต่ถ้าฉันไม่ผิดนี่คือการคาดคั้นผู้ใช้ windows อีกครั้ง โปรดนึกถึงผู้ใช้ windows!
Mathijs Segers

@MathijsSegers ฮะ! นั่นเป็นเหตุผลที่ฉันรวมลิงค์ไปยังไบนารีเพื่อให้ผู้ใช้ windows ได้รับมันด้วย! :)
Govind Rai

มีวิธีทำให้สิ่งนี้ทำงานกับไดเร็กทอรีภายในโปรเจ็กต์แทนที่จะเป็นไดเร็กทอรีคอมพิวเตอร์หรือไม่?
Matt Croak

13

Archive.bulkเลิกใช้แล้ววิธีใหม่ที่จะใช้สำหรับสิ่งนี้คือ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
พวกเขาสงสัยเกี่ยวกับเรื่องนี้พวกเขาบอกว่าจำนวนมากเลิกใช้งานแล้ว แต่ไม่ได้แนะนำว่าจะใช้ฟังก์ชันใดแทน
jarodsmk

1
คุณระบุไดเร็กทอรี "ต้นทาง" ได้อย่างไร?
Dreams

ลองใช้วิธีการด้านล่าง: jsonworld.wordpress.com/2019/09/07/…
Soni Kumari

2020: archive.directory () ง่ายกว่ามาก!
OhadR

9

ในการรวมไฟล์และไดเรกทอรีทั้งหมด:

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

โดยใช้ node-glob ( https://github.com/isaacs/node-glob ) ด้านล่างดังนั้นนิพจน์ที่ตรงกันใด ๆ ที่เข้ากันได้จะใช้ได้


.bulk เลิกใช้งานแล้ว
Mohamad Hamouday

9

นี่คือไลบรารีอื่นที่ซิปโฟลเดอร์ในบรรทัดเดียว: zip-local

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

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

4
ทำงานอย่างมีเสน่ห์ไม่เหมือนคนอื่น ๆ ที่มีอยู่บนอินเทอร์เน็ตหรือที่กล่าวถึงข้างต้นซึ่งสร้างไฟล์ 'ศูนย์ไบต์' ให้ฉันเสมอ
Sergey Pleshakov

4

เมื่อต้องการไพพ์ผลลัพธ์ไปยังอ็อบเจ็กต์การตอบสนอง (สถานการณ์ที่จำเป็นต้องดาวน์โหลด zip แทนที่จะเก็บไว้ในเครื่อง)

 archive.pipe(res);

คำแนะนำของแซมในการเข้าถึงเนื้อหาของไดเร็กทอรีใช้ได้ผลกับฉัน

src: ["**/*"]

3

Adm-zip มีปัญหาในการบีบอัดไฟล์เก็บถาวรที่มีอยู่https://github.com/cthackers/adm-zip/issues/64รวมถึงความเสียหายด้วยการบีบอัดไฟล์ไบนารี

ฉันพบปัญหาความเสียหายในการบีบอัดด้วย node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver เป็นตัวเดียวที่ดูเหมือนจะบีบอัดได้ดี แต่ไม่มีฟังก์ชันการคลายการบีบอัดใด ๆ


1
คุณกำลังพูดถึง node-archiver ตัวไหน : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe

@firian เขาไม่ได้พูดว่า Archiver เขาบอกว่า Adm-zip
Francis Pelland

5
@FrancisPelland อืมในประโยคสุดท้ายเขาเขียนว่า " node-archiver เป็นคนเดียวที่ดูเหมือนจะใช้งานได้ " นั่นคือสิ่งที่ฉันอ้างถึง
biphobe

ฉันคิดว่าเขามีเนื้อnpmjs.com/package/archiver
OhadR

2

ฉันพบห้องสมุดขนาดเล็กที่รวบรวมสิ่งที่คุณต้องการ

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


สามารถเพิ่มพารามิเตอร์เพื่อสร้างโฟลเดอร์ zip ได้หรือไม่? เช่นระดับและขนาดที่บีบอัดถ้าเป็นเช่นนั้นจะทำอย่างไร?
ตรัง

1

เนื่องจากarchiverไม่เข้ากันกับรุ่นใหม่ของ webpack เป็นเวลานานผมขอแนะนำให้ใช้ซิป 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

คุณสามารถลองทำได้ง่ายๆดังนี้

ติดตั้งzip-dir:

npm install zip-dir

และใช้มัน

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

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

    });

เป็นไปได้ไหมที่จะเพิ่มพารามิเตอร์เพื่อสร้างโฟลเดอร์ zip เช่นระดับและขนาดที่บีบอัดถ้าเป็นเช่นนั้นจะทำอย่างไร?
ตรัง

0

ฉันลงเอยด้วยการห่อไฟล์เก็บถาวรเพื่อเลียนแบบ JSZip เนื่องจากการปรับโครงสร้างใหม่ผ่านโครงการของฉันทำให้ต้องใช้ความพยายามมากเกินไป ฉันเข้าใจว่า Archiver อาจไม่ใช่ตัวเลือกที่ดีที่สุด แต่คุณไปได้เลย

// 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);
    }
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.