Необходимо заархивировать весь каталог с помощью Node.js

109

Мне нужно заархивировать весь каталог с помощью Node.js. В настоящее время я использую node-zip, и каждый раз, когда процесс запускается, он генерирует недопустимый ZIP-файл (как вы можете видеть из этой проблемы Github ).

Есть ли другой, лучший вариант Node.js, который позволит мне заархивировать каталог?

РЕДАКТИРОВАТЬ: в итоге я использовал архиватор

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>

ОБНОВЛЕНИЕ: для тех, кто интересуется реализацией, которую я использовал, вот ссылка на мой загрузчик :

ограниченный
источник
3
Кто-то в Твиттере предложил API-интерфейс child_process и просто позвонил в систему ZIP: nodejs.org/api/child_process.html
commadelimited
1
Я пробовал подход child_process. Есть два предостережения. 1)zip команда unix включает всю иерархию родительских папок текущего рабочего каталога в заархивированный файл. Это может быть нормально для тебя, это не для меня. Также изменение текущего рабочего каталога в child_process каким-то образом не влияет на результаты. 2) Чтобы решить эту проблему, вы должны использовать pushdдля перехода в папку, которую вы заархивируете zip -r, но поскольку pushd она встроена в bash, а не в / bin / sh, вам также необходимо использовать / bin / bash. В моем конкретном случае это было невозможно. Просто предупреждаю.
johnozbay
2
API узла @johnozbay child_process.execпозволяет вам указать cwd, откуда вы хотите запустить команду. Изменение CWD устраняет проблему иерархии родительских папок. Это также решает проблему отсутствия необходимости pushd. Я полностью рекомендую child_process.
Говинд Рай
1
stackoverflow.com/a/49970368/2757916 собственное решение nodejs с использованием child_process api. 2 строки кода. Никаких сторонних библиотек.
Говинд Рай
@GovindRai Большое спасибо!
johnozbay

Ответы:

128

В итоге я использовал архиватор 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 and naming it `new-subdir` within the archive (see docs for more options):
archive.directory(source_dir, false);
archive.finalize();
ограниченный
источник
1
Кажется, нет никаких примеров того, как это сделать, не могли бы вы поделиться тем, что вы сделали?
Sinetheta
1
архиватор, к сожалению, пока не поддерживает символы Unicode в именах файлов. Об этом сообщает github.com/ctalkington/node-archiver/issues/90 .
Eye
2
Как мне рекурсивно включить все файлы и каталоги (также скрытые файлы / каталоги)?
Ionică Bizău
12
Архиватор делает это еще проще. Вместо использования метода bulk () теперь вы можете использовать directory (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Джош Фельдман
14
.bulkустарело
chovy 05
49

Я не претендую на то, чтобы показывать что-то новое, просто хочу обобщить приведенные выше решения для тех, кто любит использовать функции 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();
  });
}

Надеюсь, это кому-то поможет;)

Д.Димитриогло
источник
что именно здесь "нет"? Я предполагаю, что источник - это путь к каталогу
Dreams
@Tarun полный путь zip, например: /User/mypc/mydir/test.zip
D.Dimitrioglo
Не удается распаковать zip-файл. Операция запрещена
Джейк
@ ekaj_03, пожалуйста, убедитесь, что у вас достаточно прав для указанного каталога
D.Dimitrioglo 02
1
@ D.Dimitrioglo все хорошо. Это была проблема с исходным каталогом. Спасибо :)
Джейк
17

Для этого используйте собственный child_processAPI Node .

Нет необходимости в сторонних библиотеках. Две строчки кода.

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 тоже могли его получить! :)
Говинд Рай
Есть ли способ заставить это работать для каталога в проекте, а не для каталога компьютера?
Matt
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();
caiocpricci2
источник
2
Интересно, они сказали, что массовая загрузка устарела, но не предложили, какую функцию использовать вместо нее.
jarodsmk
1
Как указать "исходный" каталог?
Dreams
Попробуйте один раз следующий подход: jsonworld.wordpress.com/2019/09/07/…
Сони Кумари
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 устарел
Мохамад Хамудей,
9

Это еще одна библиотека, которая архивирует папку в одну строку: zip-local

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

zipper.sync.zip("./hello/world/").compress().save("pack.zip");
Мечты
источник
4
Работал как шарм, в отличие от дюжины других доступных в Интернете или упомянутых выше, которые всегда генерировали для меня файл с нулевым
Сергей Плешаков
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 - единственный, который, кажется, хорошо работает для сжатия, но не имеет функции распаковки.

Xiaoxin
источник
1
О каком узле-архиваторе идет речь? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe
@firian Он не сказал Архиватор, он сказал Adm-zip.
Фрэнсис Пелланд
5
@FrancisPelland Умм, в последнем предложении он написал: « Кажется, работает только узел-архиватор » - вот о чем я говорю.
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-папки? как сжатый уровень и размер, если да, то как это сделать?
Trang D
1

Поскольку archiverон давно не совместим с новой версией webpack, рекомендую использовать 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

Можно попробовать простым способом:

Установить 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-папки? как сжатый уровень и размер, если да, то как это сделать?
Trang D
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);
    }
};
user672770
источник