Ao verificar os logs do meu próprio aplicativo, descobri que sempre demora alguns segundos para carregar depois de entrar na página de log (a interface não é paginada), então abri o painel de rede para verificar
Só então descobri que os dados retornados pela interface não estavamcompactados
. Pensei que a interface usava proxy reverso Nginx, e o Nginx me ajudaria automaticamente a fazer essa camada (explorarei isso mais tarde, é teoricamente viável).
aqui está o Node. Service.
Este artigo compartilhará conhecimento sobre HTTP数据压缩
Node侧的实践
. Todos os clientes a seguir se referem à
Quando o cliente inicia uma solicitação ao servidor, ele adicionará o campo accept-encoding
ao cabeçalho da solicitação, cujo valor indica支持的压缩内容编码
实际压缩使用的编码算法
do conteúdo, adicionando content-encoding
ao cabeçalho de resposta.
deflate
usa o algoritmo LZ77
e哈夫曼编码(Huffman Coding)
algoritmo de compressão de dados baseado na哈夫曼编码(Huffman Coding)
.
gzip
é um algoritmo baseado em DEFLATE
br
que se refere a Brotli
. Este formato de dados visa melhorar ainda mais a taxa de compactação. Em comparação com deflate
, a compactação de texto pode aumentar a densidade de compactação em 20%
, enquanto sua velocidade de compactação e descompactação permanece praticamente inalterada
Node.js contém um zlib 模块
, que fornece funções de compactação implementadas usando Gzip
, Deflate/Inflate
e Brotli
Deflate/Inflate
Brotli
gzip
é usado como exemplo para listar vários métodos de uso de acordo com os cenários. da mesma forma, mas a API é
baseada na operação stream
.
Operações baseadas em buffer
Introduzir vários módulos necessários
const zlib = require('zlib') const fs = requer('fs') const fluxo = require('fluxo') const testFile = 'testes/origin.log' const targetFile = `${testFile}.gz` Const decodeFile = `${testFile}.un.gz`visualização dos resultados
/compressão do arquivo, aqui use o comando du
para contar diretamente os resultados antes e depois da descompressão
# Execute du -ah testes # Os resultados são os seguintes 108K testes/origin.log.gz 2,2 milhões de testes/origin.log 2,2 milhões de testes/origin.log.un.gz 4.6M testa
流(stream)
usando createGzip
e createUnzip
zlib
, exceto aquelas explicitamente síncronas, usam o pool de threads interno do Node.js e podem ser consideradas assíncronas,Método 1: Use diretamente o método pipe
na instância para transferir o fluxo
// Compressão const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // Descompacta const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
Método 2: Usando pipeline
no stream
, você pode fazer outro processamento separadamente no retorno de chamada
// Compressão const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { se (errar) { console.error(erro); } }) // Descompacta const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { se (errar) { console.error(erro); } })
Método 3: método pipeline
de promessa
const { promisify } = require('util') const pipeline = promisify(stream.pipeline) // Compacta const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) pipeline (readStream, zlib.createGzip(), writeStream) .catch(err => { console.error(erro); }) // Descompacta const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) pipeline (readStream, zlib.createUnzip(), writeStream) .catch(err => { console.error(erro); })
Buffer
utilizam APIs gzip
e unzip
Esses dois同步
异步
gzip
gzipSync
unzip
unzipSync
método 1: Converta readStream
em Buffer
e, em seguida, execute operações adicionais
// Compression const buff = [] readStream.on('dados', (pedaço) => { buff.push(pedaço) }) readStream.on('fim', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { se(erro){ console.error(erro); processo.exit() } fs.writeFileSync(targetFile,resBuff) }) })
// compactação const buff = [] readStream.on('dados', (pedaço) => { buff.push(pedaço) }) readStream.on('fim', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
Método 2: Leia diretamente por meio readFileSync
// Comprimido const readBuffer = fs.readFileSync(testFile) const decodeBuffer = zlib.gzipSync(readBuffer) fs.writeFileSync(targetFile,decodeBuffer) // Descompacta const readBuffer = fs.readFileSync(targetFile) const decodeBuffer = zlib.gzipSync(decodeFile) fs.writeFileSync(targetFile,decodeBuffer)
Além da compactação de arquivo, às vezes pode ser necessário descompactar diretamente o conteúdo transmitido.
Aqui está o conteúdo de texto compactado como exemplo
// Dados de teste const testData = fs. readFileSync(testFile, {encoding: 'utf-8' })
流(stream)
, considere apenas a conversão de string
=> buffer
=> stream
string
=> buffer
const buffer = Buffer.from(testData)
buffer
=> stream
const transformStream = novo stream.PassThrough() transformStream.write (buffer) // ou const transformStream = novo stream.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)
Aqui está um exemplo de gravação em um arquivo. Claro, ele também pode ser gravado em outros fluxos, como HTTP的Response
(será apresentado separadamente posteriormente)
transformStream. .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
também usa Buffer.from
para converter a string em buffer
Buffer
const buffer = Buffer.from(testData)
e então usa diretamente a API de sincronização para conversão. content
const result = zlib.gzipSync(buffer)
pode gravar arquivos, e no HTTP Server
o conteúdo compactado também pode ser retornado diretamente
fs.writeFileSync(targetFile, result)
Aqui usamos diretamente o módulo http
no Node para criar um servidor simples Para demonstrações
em outras estruturas Node Web
, as ideias de processamento são semelhantes. Claro, geralmente existem plug-ins prontos que podem ser acessados com um clique.
const http = requer('http') const { PassThrough, pipeline } = require('stream') const zlib = requer('zlib') //Dados de teste const testTxt = 'Dados de teste 123'.repeat(1000) const app = http.createServer((req, res) => { const {url} = requerimento // Leia o algoritmo de compactação suportado const acceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) //Tipo de dados de resposta padrão res.setHeader('Content-Type', 'application/json; charset=utf-8') // Vários exemplos de rotas const rotas = [ ['/gzip', () => { if (acceptEncoding.includes('gzip')) { res.setHeader('codificação de conteúdo', 'gzip') // Use a API de sincronização para compactar diretamente o conteúdo de texto res.end(zlib.gzipSync(Buffer.from(testTxt))) retornar } res.end(testeTxt) }], ['/deflacionar', () => { if (acceptEncoding.includes('deflate')) { res.setHeader('codificação de conteúdo', 'deflate') // Operação única baseada no stream const originStream = new PassThrough() originStream.write(Buffer.from(testTxt)) originStream.pipe(zlib.createDeflate()).pipe(res) origemStream.end() retornar } res.end(testeTxt) }], ['/br', () => { if (acceptEncoding.includes('br')) { res.setHeader('codificação de conteúdo', 'br') res.setHeader('Tipo de conteúdo', 'text/html; charset=utf-8') // Múltiplas operações de gravação baseadas em streams const originStream = new PassThrough() pipeline(originStream, zlib.createBrotliCompress(), res, (err) => { se (errar) { console.error(erro); } }) originStream.write(Buffer.from('<h1>BrotliCompress</h1>')) originStream.write(Buffer.from('<h2>Dados de teste</h2>')) originStream.write(Buffer.from(testTxt)) origemStream.end() retornar } res.end(testeTxt) }] ] const rota = rotas.find(v => url.startsWith(v[0])) if (rota) { rota[1]() retornar } // Voltar ao início res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end(`<h1>404: ${url}</h1> <h2>Rota registrada</h2> <ul> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('') } </ul> `) res.end() }) app.listen(3000)