Потрібно ZIP-адресу всього каталогу за допомогою Node.js


107

Мені потрібно скопіювати весь каталог за допомогою Node.js. Зараз я використовую node-zip, і кожен раз, коли процес запускається, він генерує недійсний ZIP-файл (як ви бачите з цієї проблеми Github ).

Чи є інший, кращий варіант, Node.js, який дозволить мені розшифровувати каталог?

EDIT: Я закінчив використовувати архіватор

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
Хтось із Twitter запропонував API child_process і просто зателефонував у систему ZIP: nodejs.org/api/child_process.html
коммаделімітований

1
Я спробував підхід child_process. У нього два застереження. 1)zip Команда unix включає всю ієрархію батьківських папок поточного робочого каталогу у заархівованому файлі. Це може бути для вас нормально, це не для мене. Також зміна поточного робочого каталогу в child_process якось не впливає на результати. 2) Щоб подолати цю проблему, вам потрібно скористатися, pushdщоб перейти в папку, яку ви будете зіштовхувати zip -r, але оскільки pushd вона вбудована в bash, а не / bin / sh, вам також потрібно використовувати / bin / bash. У моєму конкретному випадку це було неможливо. Просто голови вгору.
johnozbay

2
Appi вузла @johnozbay child_process.execдозволяє вказати cwd, звідки ви хочете запустити команду. Зміна CWD вирішує проблему ієрархії батьківської папки. Він також фіксує питання про непотрібність pushd. Я повністю рекомендую child_process.
Говінд Рай

1
stackoverflow.com/a/49970368/2757916 рідної nodejs рішення з використанням child_process API. 2 рядки коду. Жодних сторонніх лайків.
Говінд Рай

@GovindRai Дякую!
johnozbay

Відповіді:


124

Я в кінцевому підсумку використовував архіватор 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
Здається, немає прикладів, як це зробити, чи не проти поділитися тим, що ви робили?
Сінетхета

1
Архівник, на жаль, не підтримує символи Unicode у назви файлів на даний момент. Повідомлено на github.com/ctalkington/node-archiver/isissue/90 .
Очі

2
Як я включаю всі файли та каталоги, рекурсивно (також приховані файли / каталоги)?
Ionică Bizău

12
Archiver робить це ще простіше. Замість того, щоб використовувати метод bulk (), тепер можна використовувати каталог (): npmjs.com/package/archiver#directory-dirpath-destpath-data
Джош Фельдман

14
.bulkзастаріло
човен

46

Я не претендую на те, що показую щось нове, просто хочу узагальнити рішення вище для тих, хто любить використовувати функції 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-файл. Експлуатація заборонена
Джейк

@ ekaj_03, будь ласка, переконайтеся, що у вас є достатньо прав на вказаний каталог
D.Dimitrioglo,

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)якщо вам потрібна асинхронізація. Є набагато більше варіантів, ніж просто вказати CWD для подальшої уточнення ваших запитів. Дивіться документи exec / execSync .


Зверніть увагу: Цей приклад передбачає, що у вашій системі встановлена ​​утиліта zip (принаймні, вона має OSX). У деяких операційних системах може не встановлена ​​утиліта (тобто, час виконання AWS Lambda не працює). У такому випадку ви можете легко отримати тут двійковий файл утиліти zip та упакувати його разом із вихідним кодом програми (для AWS Lambda ви також можете упакувати його в Lambda Layer), або вам доведеться використовувати модуль сторонніх розробників (яких в NPM багато) Я віддаю перевагу колишньому підходу, оскільки утиліту ZIP перевіряють десятиліттями.


9
На жаль, працює лише в тих системах, які є zip.
січень

3
Пішов на це рішення просто заради уникнення десятків зовнішніх бібліотек мого проекту
EAzevedo

це має сенс, але якщо я не помиляюся, це знову накручується на користувачів Windows. Подумайте про користувачів Windows!
Mathijs Segers

@MathijsSegers ха-ха! ось чому я включив посилання на бінарне, щоб користувачі Windows могли отримати його теж! :)
Говінд Рай

Чи є спосіб зробити так, щоб це працювало для каталогу в рамках проекту, а не для комп'ютерного каталогу?
Метт Крук

13

Archive.bulkтепер застаріло, новий метод, який слід використовувати для цього, - глобус :

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
Як вказати каталог "джерело"?
Мрії

Спробуйте один раз підходити нижче: 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 застарілий
Mohamad Hamouday

9

Це ще одна бібліотека, яка переміщує папку в один рядок: zip-local

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

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

4
Працював як шарм, на відміну від десятка інших, доступних в Інтернеті або згаданих вище, які завжди генерували для мене файл «нульових байтів»
Сергій Плешаков,

4

Для передачі результату об'єкту відповіді (сценарії, де потрібно завантажувати поштовий індекс, а не зберігати локально)

 archive.pipe(res);

Підказки Сема щодо доступу до вмісту каталогу працювали на мене.

src: ["**/*"]

3

У Adm-zip є проблеми зі стисненням існуючого архіву https://github.com/cthackers/adm-zip/isissue/64 , а також зіпсування стискання двійкових файлів.

Я також стикався з проблемами корупційного стиснення за допомогою node-zip https://github.com/daraosn/node-zip/isissue/4

вузол-архіватор - це єдиний, який, здається, добре працює на стиснення, але не має функцій розтискування.


1
Про який вузол-архіватор ви говорите? : github.com/archiverjs/node-archiver; github.com/richardbolt/node-archiver
biphobe

@firian Він не сказав Archiver, він сказав Adm-zip.
Френсіс Пелланд

5
@FrancisPelland Umm, в останньому реченні він написав " вузол-архіватор - це єдиний, який, здається, працює ", - це те, на що я замислююся.
біфоб

Я думаю, що він meatn 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, оскільки рефакторинг через мій проект забирає занадто багато зусиль. Я розумію, що Архіватор може бути не найкращим вибором, але ось ви йдете.

// 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);
    }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.