Проверяя журналы собственных приложений, я обнаружил, что загрузка после входа на страницу журнала всегда занимает несколько секунд (интерфейс не разбит на страницы), поэтому я открыл панель сети, чтобы проверить
Только тогда я обнаружил, что данные, возвращаемые интерфейсом, не были сжаты. Я думал, что интерфейс использует обратный прокси-сервер Nginx, и Nginx автоматически поможет мне сделать этот уровень (я исследую это позже, это теоретически возможно)
. вот Node. Service.
В этой статье мы поделимся знаниями о HTTP数据压缩
и Node侧的实践
. Все следующие клиенты относятся к
Когда клиент инициирует запрос к серверу, он добавляет поле accept-encoding
в заголовок запроса, значение которого支持的压缩内容编码
实际压缩使用的编码算法
контента, добавляет content-encoding
в заголовок ответа.
deflate
использует как алгоритм LZ77
, так и哈夫曼编码(Huffman Coding)
Алгоритм сжатия данных на основе哈夫曼编码(Huffman Coding)
.
gzip
— это алгоритм, основанный на DEFLATE
. br
относится к Brotli
. Этот формат данных направлен на дальнейшее улучшение степени сжатия. По сравнению с deflate
, сжатие текста может увеличить плотность сжатия на 20%
, при этом скорость сжатия и распаковки остается примерно неизменной.
Node. js содержит zlib 模块
, который предоставляет функции сжатия, реализованные с помощью Gzip
, Deflate/Inflate
и Brotli
Deflate/Inflate
Brotli
gzip
в качестве примера для перечисления различных методов использования в соответствии со сценариями. таким же образом, но API
основан на работе stream
.
buffer
операции
Ввести несколько обязательных модулей
const zlib = require('zlib') const fs = require('fs') константный поток = требуется («поток») const testFile = 'tests/origin.log' const targetFile = `${testFile}.gz` Const decodeFile = `${testFile}.un.gz`просмотр результатов
, здесь используйте команду du
для непосредственного подсчета результатов до и после распаковки
# Выполнение тестов du -ah # Результаты следующие: 108 тыс. тестов/origin.log.gz 2,2 млн тестов/origin.log 2,2 миллиона тестов/origin.log.un.gz 4.6M тестирует
流(stream)
операциис использованием createGzip
и createUnzip
zlib
, за исключением явно синхронных, используют внутренний пул потоков Node.js и могут считаться асинхронными,Способ 1: напрямую используйте метод pipe
в экземпляре для передачи потока
// Compression const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // Распаковка const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
Способ 2. Используя pipeline
в stream
, вы можете выполнить другую обработку отдельно в обратном вызове
// Сжатие const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { если (ошибка) { console.error(ошибка); } }) // Распаковка const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { если (ошибка) { console.error(ошибка); } })
Способ 3: метод pipeline
обещаний
const { promisify } = require('util') константный конвейер = promisify(stream.pipeline) // Сжимаем const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) конвейер (readStream, zlib.createGzip(), writeStream) .catch(ошибка => { console.error(ошибка); }) // Распаковка const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) конвейер (readStream, zlib.createUnzip (), writeStream) .catch(ошибка => { console.error(ошибка); })
Buffer
используют API-интерфейсы gzip
и unzip
. Эти два метода включают同步
и异步
gzip
gzipSync
unzip
unzipSync
Метод 1. Преобразование readStream
в Buffer
и последующее выполнение дальнейших операций.
// Compression const buff = [] readStream.on('данные', (кусок) => { buff.push(кусок) }) readStream.on('end', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { если (ошибка) { console.error(ошибка); процесс.выход() } fs.writeFileSync(targetFile,resBuff) }) })
// const сжатия buff = [] readStream.on('данные', (кусок) => { buff.push(кусок) }) readStream.on('end', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
Способ 2: Чтение напрямую через readFileSync
// Сжатая константа readBuffer = fs.readFileSync(testFile) const decodeBuffer = zlib.gzipSync(readBuffer) fs.writeFileSync(targetFile,decodeBuffer) // Распаковываем const readBuffer = fs.readFileSync(targetFile) const decodeBuffer = zlib.gzipSync(decodeFile) fs.writeFileSync(targetFile,decodeBuffer)
Помимо сжатия файла иногда может потребоваться непосредственное распаковывание передаваемого содержимого.
Вот пример сжатого текстового содержимого
// Тестовые данные const testData = fs. readFileSync( testFile, {coding: 'utf-8' })
流(stream)
операцийпросто рассмотрите преобразование string
=> buffer
= stream
string
потока => buffer
const буфер = Buffer.from(testData)
buffer
=> stream
const TransformStream = новый поток.PassThrough() TransformStream.write(буфер) // или const TransformStream = новый поток.Duplex() TransformStream.push(Buffer.from(testData)) TransformStream.push(null)
Вот пример записи в файл. Конечно, его также можно записать в другие потоки, например HTTP的Response
(будет представлен отдельно позже)
. .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
также использует Buffer.from
для преобразования строки в buffer
Buffer
const buffer = Buffer.from(testData)
, а затем напрямую использует API синхронизации для преобразования. Результатом здесь является сжатый результат. content
const result = zlib.gzipSync(buffer)
может записывать файлы, а на HTTP Server
сжатый контент также может быть возвращен напрямую
fs.writeFileSync(targetFile, result)
Здесь мы напрямую используем модуль http
в Node для создания простой сервер. Для демонстраций
в других Node Web
фреймворках идеи обработки аналогичны. Конечно, обычно существуют готовые плагины, доступ к которым можно получить одним щелчком мыши.
const http = require('http') const { PassThrough, конвейер } = require('поток') const zlib = require('zlib') //Тестовые данные const testTxt = 'Тестовые данные 123'.repeat(1000) const app = http.createServer((req, res) => { const { URL } = требование // Чтение поддерживаемого алгоритма сжатия const AcceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) //Тип данных ответа по умолчанию res.setHeader('Content-Type', 'application/json; charset=utf-8') // Несколько примеров маршрутов const маршруты = [ ['/gzip', () => { если (acceptEncoding.includes('gzip')) { res.setHeader('кодирование контента', 'gzip') // Используем API синхронизации для прямого сжатия текстового содержимого res.end(zlib.gzipSync(Buffer.from(testTxt))) возвращаться } res.end(testTxt) }], ['/deflate', () => { если (acceptEncoding.includes('deflate')) { res.setHeader('кодирование контента', 'выкачивание') // Одна операция на основе потока const originStream = new PassThrough() originStream.write(Buffer.from(testTxt)) originStream.pipe(zlib.createDeflate()).pipe(res) originStream.end() возвращаться } res.end(testTxt) }], ['/br', () => { если (acceptEncoding.includes('br')) { res.setHeader('кодирование контента', 'br') res.setHeader('Content-Type', 'text/html; charset=utf-8') // Множественные операции записи на основе потоков const originStream = new PassThrough() конвейер(originStream, zlib.createBrotliCompress(), res, (err) => { если (ошибка) { console.error(ошибка); } }) originStream.write(Buffer.from('<h1>BrotliCompress</h1>')) originStream.write(Buffer.from('<h2>Тестовые данные</h2>')) originStream.write(Buffer.from(testTxt)) originStream.end() возвращаться } res.end(testTxt) }] ] const маршрут = маршруты.find(v => url.startsWith(v[0])) если (маршрут) { маршрут[1]() возвращаться } // В начало res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end(`<h1>404: ${url}</h1> <h2>Зарегистрированный маршрут</h2> <ул> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('') } </ul> `) рез.конец() }) приложение.прослушивать(3000)