Модуль Zlib предоставляет функциональность сжатия/восстановления данных. В его основу заложен поток преобразования данных, что становится очевидным после знакомства с примером сжатия файла, приведенным в документации Node. Я слегка изменила этот пример для работы с большим файлом.
const zlib = require('zlib'); const fs = require('fs'); const gzip = zlib.createGzip(); const inp = fs.createReadStream('test.png'); var out = fs.createWriteStream('test.png.gz'); inp.pipe(gzip).pipe(out);
Входной поток напрямую связывается с выходным, а передаваемые между ними данные подвергаются сжатию gzip, то есть происходит преобразование данных, в данном случае файла PNG.
Zlib предоставляет поддержку сжатия zlib и deflate — более сложного и управляемого алгоритма сжатия. Если файлы, созданные с применением zlib, могут быть распакованы программой командной строки gunzip (или unzip), у файлов, созданных с применением deflate, такая возможность отсутствует. Для восстановления файла, сжатого в режиме deflate, придется использовать Node или другую функциональность.
Чтобы продемонстрировать функциональность сжатия и восстановления файлов, мы создадим две программы командной строки: compress и uncompress.
Первая программа сжимает файл с выбором алгоритма gzip или deflate в параметрах командной строки. Так как мы собираемся работать с параметрами командной строки, для них также следует включить модуль Commander:
const zlib = require('zlib'); const program = require('commander'); const fs = require('fs'); program .version ('0.0.1') .option ('-s, --source [file name]', 'Source File Name') .option ('-f, --file [file name]', 'Destination File name') .option ('-t, --type <mode>', /^(gzip|deflate)$/i) .parse(process.argv); var compress; if (program.type == 'deflate') compress = zlib.createDeflate(); else compress = zlib.createGzip(); var inp = fs.createReadStream(program.source); var out = fs.createWriteStream(program.file); inp.pipe(compress).pipe(out);
Такие приложения интересны и полезны (особенно в среде Windows, в которой нет встроенной реализации сжатия), но технология сжатия особенно популярна в веб-запросах. Документация Node содержит примеры функциональности Zlib с веб-запросами.
Вместо получения сжатых файлов я покажу, как отправить сжатый файл на сервер, где он затем будет восстановлен. Сейчас мы создадим клиент и сервер для сжатия большого файла PNG и отправки его с запросом HTTP. Далее сервер будет восстановить данные и сохраняет файл.
Обратите внимание: передаваемые данные читаются в массив фрагментов, который затем используется для создания нового объекта Buffer вызовом buffer.concat(). Так как мы работаем с буфером, а не с потоком, использовать функцию pipe() не удастся. Вместо этого я буду использовать вспомогательную функцию Zlib zlib.unzip, которой передается объект Buffer и функция обратного вызова. Аргументы функции обратного вызова содержат ошибку и результат. Результат также представляет собой объект Buffer, который записывается во вновь созданный поток для записи функцией write(). Чтобы программа создавала разные экземпляры файла, имя файла дополняется временной меткой.
Создание веб-сервера, который получает сжатые данные и распаковывает их в файл:
const http = require('http'); const zlib = require('zlib'); const fs = require('fs'); const server = http.createServer().listen(8080); server.on('request', function(request,response) { if (request.method == 'POST') { var chunks = []; request.on('data', function(chunk) { chunks.push(chunk); }); request.on('end', function() { var buf = Buffer.concat(chunks); zlib.unzip(buf, function(err, result) { if (err) { response.writeHead(500); response.end(); return console.log('error ' + err); } var timestamp = Date.now(); var filename = './done' + timestamp + '.png'; fs.createWriteStream(filename).write(result); }); response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Received and undecompressed file\n'); }); } }); console.log('server listening on 8080');
Ключевой момент клиентского кода в листинге — назначение правильной кодировки Content-Encoding в заголовке. Заголовок должен содержать значение ‘gzip,deflate’. Также заголовок Content-Type: ‘application/javascript’.
Клиент сжимает файл и передает его с веб-запросом:
const http = require('http'); const fs = require('fs'); const zlib = require('zlib'); const gzip = zlib.createGzip(); const options = { hostname: 'localhost', port: 8124, method: 'POST', headers: { 'Content-Type': 'application/javascript', 'Content-Encoding': 'gzip,deflate' } }; const req = http.request(options, function(res) { res.setEncoding('utf8'); var data = ''; res.on('data', function (chunk) { data+=chunk; }); res.on('end', function() { console.log(data) }) }); req.on('error', function(e) { console.log('problem with request: ' + e.message); }); // Потоковая передача сжатого файла серверу var readable = fs.createReadStream('./test.png'); readable.pipe(gzip).pipe(req);
Клиент открывает файл для сжатия и передает его в поток преобразования данных, выполняющий сжатие Zlib; результат передается в веб-запрос (который представляет собой поток для записи). В коде мы работаем исключительно с потоками, что позволяет нам использовать функциональность pipe(), которая уже использовалась нами ранее. Использовать ее с сервером не удастся, потому что данные передаются в виде буферных фрагментов.
Буферизация файла в памяти может создать проблемы с масштабированием, поэтому возможно другое решение: сохранить несжатый файл, распаковать его и затем удалить временный несжатый файл. Оставляю его для самостоятельной работы.
Комментарии