Cần ZIP toàn bộ thư mục bằng Node.js


107

Tôi cần nén toàn bộ thư mục bằng Node.js. Tôi hiện đang sử dụng node-zip và mỗi lần quá trình chạy nó tạo ra một tệp ZIP không hợp lệ (như bạn có thể thấy từ sự cố Github này ).

Có tùy chọn Node.js khác tốt hơn cho phép tôi ZIP lên một thư mục không?

CHỈNH SỬA: Tôi đã kết thúc bằng cách sử dụng trình lưu trữ

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

giá trị mẫu cho các tham số:

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

CẬP NHẬT: Đối với những người hỏi về cách triển khai mà tôi đã sử dụng, đây là liên kết đến trình tải xuống của tôi :


3
Ai đó trên Twitter đã đề xuất API child_process và chỉ cần gọi hệ thống là ZIP: nodejs.org/api/child_process.html
commadelimited

1
Tôi đã thử cách tiếp cận child_process. Nó có hai lưu ý. 1)zip Lệnh unix bao gồm tất cả hệ thống phân cấp thư mục mẹ của thư mục làm việc hiện tại trong tệp nén. Điều này có thể ổn cho bạn, nó không phù hợp với tôi. Ngoài ra, việc thay đổi thư mục làm việc hiện tại trong child_process bằng cách nào đó không ảnh hưởng đến kết quả. 2) Để khắc phục vấn đề này, bạn phải sử dụng pushdđể nhảy vào thư mục bạn sẽ nén và zip -r, nhưng vì pushd được tích hợp vào bash chứ không phải / bin / sh nên bạn cần sử dụng / bin / bash. Trong trường hợp cụ thể của tôi, điều này là không thể. Chỉ cần một cái nhìn lên.
johnozbay

2
Api của nút @johnozbay child_process.execcho phép bạn chỉ định cwd từ nơi bạn muốn chạy lệnh. Việc thay đổi CWD sẽ khắc phục được sự cố của hệ thống phân cấp thư mục mẹ. Nó cũng khắc phục sự cố không cần pushd. Tôi hoàn toàn đề xuất quy trình con.
Govind Rai

1
stackoverflow.com/a/49970368/2757916 giải pháp nodejs gốc sử dụng child_process api. 2 dòng mã. Không nói dối bên thứ ba.
Govind Rai

@GovindRai Rất cám ơn!
johnozbay

Câu trả lời:


123

Tôi đã kết thúc bằng cách sử dụng archiver lib. Hoạt động tuyệt vời.

Thí dụ

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
Dường như không có bất kỳ ví dụ nào về cách làm điều này, bạn có phiền chia sẻ những gì bạn đã làm không?
Sinetheta

1
Thật không may, archiver không hỗ trợ các ký tự Unicode trong tên tệp hiện tại. Đã báo cáo cho github.com/ctalkington/node-archiver/issues/90 .
Eye

2
Làm cách nào để bao gồm tất cả các tệp và thư mục một cách đệ quy (cũng như các tệp / thư mục ẩn)?
Ionică Bizău

12
Archiver làm cho việc này trở nên đơn giản hơn bây giờ. Thay vì sử dụng phương pháp số lượng lớn (), bây giờ bạn có thể sử dụng thư mục (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Josh Feldman

14
.bulkkhông được dùng nữa
chovy

46

Tôi không giả vờ hiển thị một cái gì đó mới, chỉ muốn tóm tắt các giải pháp ở trên cho những người thích sử dụng các hàm Promise trong mã của họ (như tôi).

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

Hy vọng nó sẽ giúp ích cho ai đó;)


chính xác thì "ra" ở đây là gì? tôi giả định nguồn là con đường của thư mục
Dreams

Con đường @Tarun đầy đủ zip của như: /User/mypc/mydir/test.zip
D.Dimitrioglo

Không thể giải nén tệp zip. Hoạt động không được phép
Jake

@ ekaj_03 vui lòng đảm bảo rằng bạn có đủ quyền cho thư mục được chỉ định
D.Dimitrioglo

1
@ D.Dimitrioglo đều tốt. Đó là vấn đề dir nguồn. Cảm ơn :)
Jake

17

Sử dụng child_processapi gốc của Node để thực hiện điều này.

Không cần lib của bên thứ ba. Hai dòng mã.

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

Tôi đang sử dụng API đồng bộ. Bạn có thể sử dụng child_process.exec(path, options, callback)nếu bạn cần không đồng bộ. Có rất nhiều lựa chọn khác ngoài việc chỉ định CWD để tinh chỉnh thêm các yêu cầu của bạn. Xem tài liệu executive / executeSync .


Xin lưu ý: Ví dụ này giả định rằng bạn đã cài đặt tiện ích zip trên hệ thống của mình (ít nhất nó đi kèm với OSX). Một số hệ điều hành có thể không được cài đặt tiện ích (tức là không cài đặt AWS Lambda runtime). Trong trường hợp đó, bạn có thể dễ dàng lấy tệp nhị phân tiện ích zip tại đây và đóng gói nó cùng với mã nguồn ứng dụng của bạn (đối với AWS Lambda, bạn cũng có thể đóng gói nó trong một Lớp Lambda) hoặc bạn sẽ phải sử dụng mô-đun của bên thứ ba (trong đó có rất nhiều trên NPM). Tôi thích cách tiếp cận cũ hơn, vì tiện ích ZIP đã được thử nghiệm và kiểm tra trong nhiều thập kỷ.


9
Thật không may, chỉ hoạt động trên các hệ thống có zip.
janpio

3
Đã sử dụng giải pháp này chỉ vì mục đích tránh hàng tá thư viện bên ngoài trong dự án của tôi
EAzevedo

nó có ý nghĩa, nhưng nếu tôi không nhầm thì điều này lại làm cho người dùng cửa sổ vặn vẹo. Hãy nghĩ về những người sử dụng cửa sổ!
Mathijs Segers

@MathijsSegers haha! đó là lý do tại sao tôi bao gồm một liên kết đến nhị phân để người dùng windows cũng có thể nhận được nó! :)
Govind Rai

Có cách nào để điều này hoạt động cho một thư mục trong một dự án hơn là một thư mục máy tính không?
Matt Croak

13

Archive.bulkhiện không được dùng nữa, phương pháp mới sẽ được sử dụng cho việc này là global :

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
Đang băn khoăn về điều này, họ nói rằng số lượng lớn không được dùng nữa nhưng không đề xuất sử dụng chức năng nào thay thế.
jarodsmk

1
Làm thế nào để bạn chỉ định thư mục "nguồn"?
Những giấc mơ vào

Hãy thử một lần dưới đây cách tiếp cận: jsonworld.wordpress.com/2019/09/07/...
Soni Kumari

2020: archive.directory () đơn giản hơn nhiều!
OhadR

9

Để bao gồm tất cả các tệp và thư mục:

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

Nó sử dụng node-global ( https://github.com/isaacs/node-glob ) bên dưới, vì vậy bất kỳ biểu thức phù hợp nào tương thích với biểu thức đó sẽ hoạt động.


.bulk không được dùng nữa
Mohamad Hamouday

9

Đây là một thư viện khác nén thư mục trong một dòng: zip-local

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

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

4
Làm việc như một sự quyến rũ, không giống như hàng tá những người khác có sẵn trên internet hoặc được đề cập ở trên, luôn tạo ra tệp 'không byte' cho tôi
Sergey Pleshakov 12/04/19

4

Để chuyển kết quả đến đối tượng phản hồi (các trường hợp cần tải xuống tệp nén thay vì lưu trữ cục bộ)

 archive.pipe(res);

Những gợi ý của Sam để truy cập nội dung của thư mục đã phù hợp với tôi.

src: ["**/*"]

3

Adm-zip gặp sự cố khi nén bản lưu trữ hiện có https://github.com/cthackers/adm-zip/issues/64 cũng như lỗi khi nén các tệp nhị phân.

Tôi cũng đã gặp phải sự cố hỏng nén với node-zip https://github.com/daraosn/node-zip/issues/4

node-archiver là cái duy nhất có vẻ hoạt động tốt để nén nhưng nó không có bất kỳ chức năng giải nén nào.


1
Bạn đang nói về trình lưu trữ nút nào? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe

@firian Anh ấy không nói Archiver, anh ấy nói Adm-zip.
Francis Pelland,

5
@FrancisPelland Umm, trong câu cuối cùng anh ấy đã viết " node-archiver là công cụ duy nhất có vẻ hoạt động " - đó là điều tôi đang tham khảo.
biphobe

tôi nghĩ anh ấy thịt npmjs.com/package/archiver
OhadR

2

Tôi đã tìm thấy thư viện nhỏ này đóng gói những gì bạn cần.

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


Có thể thêm thông số để tạo thư mục zip không? như mức độ nén và kích thước nếu vậy làm thế nào để làm điều đó?
Trang D

1

archiverkhông tương thích với phiên bản webpack mới trong một thời gian dài, tôi khuyên bạn nên sử dụng 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

Bạn có thể thử một cách đơn giản:

Cài đặt zip-dir:

npm install zip-dir

và sử dụng nó

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

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

    });

có thể thêm các thông số để tạo thư mục zip? như mức độ nén và kích thước nếu vậy làm thế nào để làm điều đó?
Trang D

0

Tôi đã kết thúc việc gói archiver để mô phỏng JSZip, vì việc tái cấu trúc thông qua dự án của tôi sẽ tốn quá nhiều công sức. Tôi hiểu Archiver có thể không phải là sự lựa chọn tốt nhất, nhưng bạn hiểu rồi đấy.

// 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);
    }
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.