Самый простой способ загрузить и распаковать файлы в кросс-платформе Node.js?
Просто найдите простое решение для загрузки и распаковки файлов .zip
или .tar.gz
в Node.js в любой операционной системе.
Не уверен, что он встроен, или мне нужно использовать отдельную библиотеку. Есть идеи? Ищете только пару строк кода, поэтому, когда следующий zip файл приходит, который я хочу загрузить в node, это не проблема. Почувствуйте, как это должно быть легко и/или встроено, но я ничего не могу найти. Спасибо!
Ответы
Ответ 1
Оформить заказ adm-zip.
ADM-ZIP - это чистая реализация JavaScript для сжатия ZIP-данных для NodeJS.
Библиотека позволяет вам:
распаковать zip файлы непосредственно на диск или в буферы памяти сжать файлы и сохранять их на диск в формате .zip или в сжатых буферах обновить содержимое/добавить новые/удалить файлы из существующего .zip
Ответ 2
Node имеет встроенную поддержку gzip и deflate через модуль zlib:
var zlib = require('zlib');
zlib.gunzip(gzipBuffer, function(err, result) {
if(err) return console.error(err);
console.log(result);
});
Изменить: Вы можете даже pipe
передавать данные непосредственно через, например. Gunzip
(используя request):
var request = require('request'),
zlib = require('zlib'),
fs = require('fs'),
out = fs.createWriteStream('out');
// Fetch http://example.com/foo.gz, gunzip it and store the results in 'out'
request('http://example.com/foo.gz').pipe(zlib.createGunzip()).pipe(out);
Для tar-архивов существует файл tar-модуля Isaacs, который используется npm.
Изменить 2: Обновленный ответ как zlib
не поддерживает формат zip
. Это будет работать только для gzip
.
Ответ 3
yauzl - это надежная библиотека для распаковки. Принципы проектирования:
- Следуйте спецификации. Не сканировать локальные заголовки файлов. Прочтите центральный каталог метаданных файла.
- Не блокируйте поток JavaScript. Использовать и предоставлять асинхронные API.
- Сохранять использование памяти под контролем. Не пытайтесь одновременно копировать все файлы в ОЗУ.
- Никогда не сбой (если используется правильно). Не позволяйте malformed zip файлам сбивать клиентские приложения, которые пытаются поймать ошибки.
- Поймать записи небезопасных файлов. Запись в zip файле вызывает ошибку, если имя файла начинается с "/" или/[A-Za-z]://или содержит сегменты пути ".." или "\" (по спецификации).
В настоящее время имеется 97% охвата тестирования.
Ответ 4
Это 2017 год (26 октября, если быть точным).
Для древней и широко распространенной технологии, такой как unzip, я ожидаю, что там будет существовать довольно популярная, зрелая библиотека node.js unzip, которая "застойна" и "не поддерживается", потому что она "завершена".
Однако большинство библиотек кажутся либо ужасными, либо компромиссными, как всего несколько месяцев назад. Это довольно важно... поэтому я просмотрел несколько распакованных библиотек, прочитал их документы и попробовал свои примеры, чтобы попытаться выяснить WTF. Например, я пробовал эти:
Верхняя Рекомендация: yauzl
Отлично работает для полностью загруженного файла. Не так хорошо для потоковой передачи.
Хорошо документировано. Работает хорошо. Имеет смысл.
2nd Pick: node-stream-zip
antelle node-stream-zip
кажется лучшим
Установка:
npm install --save node-stream-zip
Использование:
'use strict';
var StreamZip = require('node-stream-zip');
var zip = new StreamZip({
file: './example.zip'
, storeEntries: true
});
zip.on('error', function (err) { console.error('[ERROR]', err); });
zip.on('ready', function () {
console.log('All entries read: ' + zip.entriesCount);
//console.log(zip.entries());
});
zip.on('entry', function (entry) {
var pathname = path.resolve('./temp', entry.name);
if (/\.\./.test(path.relative('./temp', pathname))) {
console.warn("[zip warn]: ignoring maliciously crafted paths in zip file:", entry.name);
return;
}
if ('/' === entry.name[entry.name.length - 1]) {
console.log('[DIR]', entry.name);
return;
}
console.log('[FILE]', entry.name);
zip.stream(entry.name, function (err, stream) {
if (err) { console.error('Error:', err.toString()); return; }
stream.on('error', function (err) { console.log('[ERROR]', err); return; });
// example: print contents to screen
//stream.pipe(process.stdout);
// example: save contents to file
mkdirp(path.dirname(pathname, function (err) {
stream.pipe(fs.createWriteStream(pathname));
});
});
});
Предупреждение о безопасности:
Не уверен, что это проверит entry.name
для вредоносных путей, которые будут исправляться некорректно (например, ../../../foo
или /etc/passwd
).
Вы можете легко проверить это самостоятельно, сравнив /\.\./.test(path.relative('./to/dir', path.resolve('./to/dir', entry.name)))
.
Плюсы: (Почему я думаю, что это лучше?)
- может распаковать обычные файлы (возможно, не какие-то сумасшедшие с странными расширениями)
- может передавать поток
- Кажется, не нужно загружать весь zip для чтения записей
- имеет примеры в стандартном JavaScript (не компилируется)
- не включает кухонную раковину (т.е. загрузку URL, слои S3 или db).
- использует какой-то существующий код из популярной библиотеки
- не содержит слишком много бессмысленного hipster или ninja-foo в коде
против
- Проглатывает ошибки, как голодный бегемот
- Выбрасывает строки вместо ошибок (нет трассировок стека)
-
zip.extract()
, похоже, не работает (поэтому я использовал zip.stream()
в моем примере)
Бегущий вверх: node -unzipper
Установка:
npm install --save unzipper
Использование:
'use strict';
var fs = require('fs');
var unzipper = require('unzipper');
fs.createReadStream('./example.zip')
.pipe(unzipper.Parse())
.on('entry', function (entry) {
var fileName = entry.path;
var type = entry.type; // 'Directory' or 'File'
console.log();
if (/\/$/.test(fileName)) {
console.log('[DIR]', fileName, type);
return;
}
console.log('[FILE]', fileName, type);
// TODO: probably also needs the security check
entry.pipe(process.stdout/*fs.createWriteStream('output/path')*/);
// NOTE: To ignore use entry.autodrain() instead of entry.pipe()
});
Pros
- Кажется, работает аналогично
node-stream-zip
, но меньше контроля
- Более функциональный fork
unzip
- Кажется, что он запускается серийно, а не параллельно.
против
- Кухонная мойка? Просто включает в себя массу вещей, которые не связаны с распаковкой.
- Читает весь файл (куском, что хорошо), а не просто случайные запросы
Ответ 5
Я попробовал несколько из unzip-библиотек nodejs, включая adm-zip и unzip, а затем установил на extract-zip, который является оберткой вокруг yauzl. Казалось, что проще всего реализовать.
https://www.npmjs.com/package/extract-zip
var extract = require('extract-zip')
extract(zipfile, { dir: outputPath }, function (err) {
// handle err
})
Ответ 6
Я долго ждал этого и не нашел простого рабочего примера, но на основе этих ответов я создал функцию downloadAndUnzip()
.
Использование довольно просто:
downloadAndUnzip('http://your-domain.com/archive.zip', 'yourfile.xml')
.then(function (data) {
console.log(data); // unzipped content of yourfile.xml in root of archive.zip
})
.catch(function (err) {
console.error(err);
});
И вот объявление:
var AdmZip = require('adm-zip');
var request = require('request');
var downloadAndUnzip = function (url, fileName) {
/**
* Download a file
*
* @param url
*/
var download = function (url) {
return new Promise(function (resolve, reject) {
request({
url: url,
method: 'GET',
encoding: null
}, function (err, response, body) {
if (err) {
return reject(err);
}
resolve(body);
});
});
};
/**
* Unzip a Buffer
*
* @param buffer
* @returns {Promise}
*/
var unzip = function (buffer) {
return new Promise(function (resolve, reject) {
var resolved = false;
var zip = new AdmZip(buffer);
var zipEntries = zip.getEntries(); // an array of ZipEntry records
zipEntries.forEach(function (zipEntry) {
if (zipEntry.entryName == fileName) {
resolved = true;
resolve(zipEntry.getData().toString('utf8'));
}
});
if (!resolved) {
reject(new Error('No file found in archive: ' + fileName));
}
});
};
return download(url)
.then(unzip);
};
Ответ 7
Другой рабочий пример:
var zlib = require('zlib');
var tar = require('tar');
var ftp = require('ftp');
var files = [];
var conn = new ftp();
conn.on('connect', function(e)
{
conn.auth(function(e)
{
if (e)
{
throw e;
}
conn.get('/tz/tzdata-latest.tar.gz', function(e, stream)
{
stream.on('success', function()
{
conn.end();
console.log("Processing files ...");
for (var name in files)
{
var file = files[name];
console.log("filename: " + name);
console.log(file);
}
console.log("OK")
});
stream.on('error', function(e)
{
console.log('ERROR during get(): ' + e);
conn.end();
});
console.log("Reading ...");
stream
.pipe(zlib.createGunzip())
.pipe(tar.Parse())
.on("entry", function (e)
{
var filename = e.props["path"];
console.log("filename:" + filename);
if( files[filename] == null )
{
files[filename] = "";
}
e.on("data", function (c)
{
files[filename] += c.toString();
})
});
});
});
})
.connect(21, "ftp.iana.org");
Ответ 8
Я нашел успех со следующим, работает с .zip
(Упрощается здесь для публикации: нет проверки ошибок и просто распаковывает все файлы в текущую папку)
function DownloadAndUnzip(URL){
var unzip = require('unzip');
var http = require('http');
var request = http.get(URL, function(response) {
response.pipe(unzip.Extract({path:'./'}))
});
}