Lors de la vérification de mes propres journaux d'application, j'ai constaté que le chargement prend toujours quelques secondes après être entré dans la page de journal (l'interface n'est pas paginée), j'ai donc ouvert le panneau réseau pour vérifier
Ce
n'est qu'alors que j'ai découvert que les données renvoyées par l'interface n'étaient pas compressées. Je pensais que l'interface utilisait le proxy inverse Nginx, et Nginx m'aiderait automatiquement à faire cette couche (j'explorerai cela plus tard, c'est théoriquement faisable).
Voici Node.ServiceCet
article partagera des connaissances sur HTTP数据压缩
Node侧的实践
clients suivants font tous référence au navigateur
Lorsque le client initie une requête au serveur, il ajoute le champ accept-encoding
à l'en-tête de la requête, dont la valeur indique支持的压缩内容编码
实际压缩使用的编码算法
du contenu en ajoutant content-encoding
à l'en-tête de réponse.
deflate
utilise à la fois l'algorithme LZ77
et哈夫曼编码(Huffman Coding)
algorithme de compression de données basé sur哈夫曼编码(Huffman Coding)
.
gzip
est un algorithme basé sur DEFLATE
br
fait référence à Brotli
. Ce format de données vise à améliorer encore le taux de compression. Par rapport à deflate
, la compression du texte peut augmenter la densité de compression de 20%
, tandis que sa vitesse de compression et de décompression reste à peu près inchangée
Node. js contient un zlib 模块
, qui fournit des fonctions de compression implémentées à l'aide de Gzip
, Deflate/Inflate
et Brotli
Deflate/Inflate
Brotli
gzip
est utilisé comme exemple pour répertorier diverses méthodes d'utilisation selon les scénarios utilisés dans le. de la même manière, mais l'API est
basée sur le fonctionnement stream
.
Opérations basées sur buffer
Introduire plusieurs modules requis
const zlib = require('zlib') const fs = exiger('fs') const flux = exiger('flux') const testFile = 'tests/origine.log' const targetFile = `${testFile}.gz` Const decodeFile = `${testFile}.un.gz`vue des résultats
/compression du fichier, utilisez ici la commande du
pour compter directement les résultats avant et après la décompression
# Exécuter les tests du -ah # Les résultats sont les suivants 108K tests/origin.log.gz 2,2 millions de tests/origin.log 2,2 millions de tests/origin.log.un.gz 4,6 millions testent
流(stream)
à l'aide de createGzip
et createUnzip
zlib
, à l'exception de celles explicitement synchrones, utilisent le pool de threads interne de Node.js et peuvent être considérées comme asynchrones,Méthode 1 : Utiliser directement la méthode pipe
sur l'instance pour transférer le flux
// Compression const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream (targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // Décompresser const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream (decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
Méthode 2 : En utilisant pipeline
sur stream
, vous pouvez effectuer d'autres traitements séparément dans le rappel
// Compression const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream (targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { si (erreur) { console.erreur(erreur); } }) // Décompresser const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream (decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { si (erreur) { console.erreur(erreur); } })
Méthode 3 : méthode pipeline
de promesse
const { promisify } = require('util') const pipeline = promisify(stream.pipeline) // Compresser const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream (targetFile) pipeline (readStream, zlib.createGzip(), writeStream) .catch(erreur => { console.erreur(erreur); }) // Décompresser const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream (decodeFile) pipeline (readStream, zlib.createUnzip(), writeStream) .catch(erreur => { console.erreur(erreur); })
Buffer
utilisent les API gzip
et unzip
. Ces deux同步
异步
gzip
gzipSync
unzip
unzipSync
méthode 1 : convertissez readStream
en Buffer
, puis effectuez d'autres opérations
// Compression const buff = [] readStream.on('data', (morceau) => { buff.push (morceau) }) readStream.on('end', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { si(erreur){ console.erreur(erreur); processus.exit() } fs.writeFileSync(targetFile,resBuff) }) })
// compression const buff = [] readStream.on('data', (morceau) => { buff.push (morceau) }) readStream.on('end', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
Méthode 2 : Lire directement via readFileSync
// Const compressé readBuffer = fs.readFileSync(testFile) const decodeBuffer = zlib.gzipSync(readBuffer) fs.writeFileSync(targetFile,decodeBuffer) // Décompresser const readBuffer = fs.readFileSync(targetFile) const decodeBuffer = zlib.gzipSync(decodeFile) fs.writeFileSync(targetFile,decodeBuffer)
En plus de la compression de fichiers, il peut parfois être nécessaire de décompresser directement le contenu transmis
Voici le contenu texte compressé à titre d'exemple
// Données de test const testData = fs. readFileSync( testFile, { encoding: 'utf-8' })
流(stream)
, considérez simplement la conversion de string
=> buffer
=> stream
string
=> buffer
const buffer = Buffer.from(testData)
buffer
=> stream
const transformStream = nouveau flux.PassThrough() transformStream.write(tampon) // ou const transformStream = nouveau stream.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)
Voici un exemple d'écriture dans un fichier. Bien sûr, il peut également être écrit dans d'autres flux, tels que HTTP的Response
(sera présenté séparément plus tard)
transformStream. .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
utilise également Buffer.from
pour convertir la chaîne en buffer
Buffer
const buffer = Buffer.from(testData)
puis utilise directement l'API de synchronisation pour la conversion. Le résultat ici est le compressé. content
const result = zlib.gzipSync(buffer)
peut écrire des fichiers, et dans HTTP Server
le contenu compressé peut également être renvoyé directement
fs.writeFileSync(targetFile, result)
Ici, nous utilisons directement le module http
dans Node pour créer un simple serveur Pour les démonstrations
dans d'autres frameworks Node Web
, les idées de traitement sont similaires. Bien entendu, il existe généralement des plug-ins prêts à l'emploi accessibles en un seul clic.
const http = exiger('http') const { PassThrough, pipeline } = require('stream') const zlib = exiger('zlib') //Données de test const testTxt = 'Données de test 123'.repeat(1000) const app = http.createServer((req, res) => { const {url} = req // Lire l'algorithme de compression pris en charge const acceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) //Type de données de réponse par défaut res.setHeader('Content-Type', 'application/json; charset=utf-8') // Plusieurs exemples de routes const routes = [ ['/gzip', () => { if (acceptEncoding.includes('gzip')) { res.setHeader('content-encoding', 'gzip') // Utiliser l'API de synchronisation pour compresser directement le contenu texte res.end(zlib.gzipSync(Buffer.from(testTxt))) retour } res.end(testTxt) }], ['/dégonfler', () => { if (acceptEncoding.includes('deflate')) { res.setHeader('content-encoding', 'deflate') // Opération unique basée sur stream const originStream = new PassThrough() originStream.write(Buffer.from(testTxt)) originStream.pipe(zlib.createDeflate()).pipe(res) originStream.end() retour } res.end(testTxt) }], ['/br', () => { if (acceptEncoding.includes('br')) { res.setHeader('content-encoding', 'br') res.setHeader('Content-Type', 'text/html; charset=utf-8') // Opérations d'écriture multiples basées sur les flux const originStream = new PassThrough() pipeline(originStream, zlib.createBrotliCompress(), res, (err) => { si (erreur) { console.erreur(erreur); } }) originStream.write(Buffer.from('<h1>BrotliCompress</h1>')) originStream.write(Buffer.from('<h2>Données de test</h2>')) originStream.write(Buffer.from(testTxt)) originStream.end() retour } res.end(testTxt) }] ] const route = routes.find(v => url.startsWith(v[0])) si (itinéraire) { itinéraire[1]() retour } // Retour en haut res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end(`<h1>404 : ${url}</h1> <h2>Itinéraire enregistré</h2> <ul> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('') } </ul> `) res.end() }) app.écouter (3000)