溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

發(fā)布時(shí)間:2022-03-10 11:18:45 來源:億速云 閱讀:275 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了如何利用Node實(shí)現(xiàn)內(nèi)容壓縮,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

在查看自己的應(yīng)用日志時(shí),發(fā)現(xiàn)進(jìn)入日志頁面后總是要幾秒鐘才會加載(接口沒做分頁),于是打開網(wǎng)絡(luò)面板查看

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

這才發(fā)現(xiàn)接口返回的數(shù)據(jù)都沒有被壓縮,本以為接口用Nginx反向代理了,Nginx會自動幫我做這一層(這塊后面探究一下,理論上是可行的)

這里的后端是 Node 服務(wù)

前置知識

下面的客戶端均指瀏覽器

accept-encoding

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

客戶端在向服務(wù)端發(fā)起請求時(shí),會在請求頭(request header)中添加accept-encoding字段,其值標(biāo)明客戶端支持的壓縮內(nèi)容編碼格式

content-encoding

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

服務(wù)端在對返回內(nèi)容執(zhí)行壓縮后,通過在響應(yīng)頭(response header)中添加content-encoding,來告訴瀏覽器內(nèi)容實(shí)際壓縮使用的編碼算法

deflate/gzip/br

deflate是同時(shí)使用了LZ77算法與哈夫曼編碼(Huffman Coding)的一個(gè)無損數(shù)據(jù)壓縮算法。

gzip 是基于 DEFLATE 的算法

br指代Brotli,該數(shù)據(jù)格式旨在進(jìn)一步提高壓縮比,對文本的壓縮相對deflate能增加20%的壓縮密度,而其壓縮與解壓縮速度則大致不變

zlib模塊

Node.js包含一個(gè)zlib 模塊,提供了使用 Gzip、Deflate/Inflate、以及 Brotli 實(shí)現(xiàn)的壓縮功能

這里以gzip為例分場景列舉多種使用方式,Deflate/InflateBrotli使用方式一樣,只是API不一樣

基于stream的操作

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

基于buffer的操作

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

引入幾個(gè)所需的模塊

const zlib = require('zlib')
const fs = require('fs')
const stream = require('stream')
const testFile = 'tests/origin.log'
const targetFile = `${testFile}.gz`
const decodeFile = `${testFile}.un.gz`

文件的解/壓縮

解/壓縮結(jié)果查看,這里使用du指令直接統(tǒng)計(jì)解壓縮前后結(jié)果

# 執(zhí)行
du -ah tests

# 結(jié)果如下
108K    tests/origin.log.gz
2.2M    tests/origin.log
2.2M    tests/origin.log.un.gz
4.6M    tests

基于流(stream)的操作

使用createGzipcreateUnzip

  • 注:所有 zlib API,除了那些顯式同步的 API,都使用 Node.js 內(nèi)部線程池,可以看做是異步的

  • 因此下面的示例中的壓縮和解壓代碼應(yīng)分開執(zhí)行,否則會報(bào)錯(cuò)

方式1: 直接利用實(shí)例上的pipe方法傳遞流

// 壓縮
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: 利用stream上的pipeline,可在回掉中單獨(dú)做其它的處理

// 壓縮
const readStream = fs.createReadStream(testFile)
const writeStream = fs.createWriteStream(targetFile)
stream.pipeline(readStream, zlib.createGzip(), writeStream, err => {
    if (err) {
        console.error(err);
    }
})

// 解壓
const readStream = fs.createReadStream(targetFile)
const writeStream = fs.createWriteStream(decodeFile)
stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => {
    if (err) {
        console.error(err);
    }
})

方式3: Promise化pipeline方法

const { promisify } = require('util')
const pipeline = promisify(stream.pipeline)

// 壓縮
const readStream = fs.createReadStream(testFile)
const writeStream = fs.createWriteStream(targetFile)
pipeline(readStream, zlib.createGzip(), writeStream)
    .catch(err => {
        console.error(err);
    })

// 解壓
const readStream = fs.createReadStream(targetFile)
const writeStream = fs.createWriteStream(decodeFile)
pipeline(readStream, zlib.createUnzip(), writeStream)
    .catch(err => {
        console.error(err);
    })

基于Buffer的操作

利用 gzipunzip API,這兩個(gè)方法包含同步異步類型

  • 壓縮

    • gzip

    • gzipSync

  • 解壓

    • unzip

    • unzipSync

方式1:readStream轉(zhuǎn)Buffer,然后進(jìn)行進(jìn)一步操作

  • gzip:異步

// 壓縮
const buff = []
readStream.on('data', (chunk) => {
    buff.push(chunk)
})
readStream.on('end', () => {
    zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => {
        if(err){
            console.error(err);
            process.exit()
        }
        fs.writeFileSync(targetFile,resBuff)
    })
})
  • gzipSync:同步

// 壓縮
const buff = []
readStream.on('data', (chunk) => {
    buff.push(chunk)
})
readStream.on('end', () => {
    fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff)))
})

方式2: 直接通過readFileSync讀取

// 壓縮
const 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)

文本內(nèi)容的解/壓縮

除了對文件壓縮,有時(shí)候也許要對傳輸?shù)膬?nèi)容進(jìn)行直接進(jìn)行解壓縮

這里以壓縮文本內(nèi)容為例

// 測試數(shù)據(jù)
const testData = fs.readFileSync(testFile, { encoding: 'utf-8' })

基于流(stream)操作

這塊就考慮 string =>  buffer => stream的轉(zhuǎn)換就行

string =>  buffer

const buffer = Buffer.from(testData)

buffer => stream

const transformStream = new stream.PassThrough()
transformStream.write(buffer)

// or
const transformStream = new stream.Duplex()
transformStream.push(Buffer.from(testData))
transformStream.push(null)

這里以寫入到文件示例,當(dāng)然也可以寫到其它的流里,如HTTP的Response(后面會單獨(dú)介紹)

transformStream
    .pipe(zlib.createGzip())
    .pipe(fs.createWriteStream(targetFile))

基于Buffer操作

同樣利用Buffer.from將字符串轉(zhuǎn)buffer

const buffer = Buffer.from(testData)

然后直接使用同步API進(jìn)行轉(zhuǎn)換,這里result就是壓縮后的內(nèi)容

const result = zlib.gzipSync(buffer)

可以寫入文件,在HTTP Server中也可直接對壓縮后的內(nèi)容進(jìn)行返回

fs.writeFileSync(targetFile, result)

Node Server中的實(shí)踐

這里直接使用Node中 http 模塊創(chuàng)建一個(gè)簡單的 Server 進(jìn)行演示

在其他的 Node Web 框架中,處理思路類似,當(dāng)然一般也有現(xiàn)成的插件,一鍵接入

如何利用Node實(shí)現(xiàn)內(nèi)容壓縮

const http = require('http')
const { PassThrough, pipeline } = require('stream')
const zlib = require('zlib')

// 測試數(shù)據(jù)
const testTxt = '測試數(shù)據(jù)123'.repeat(1000)

const app = http.createServer((req, res) => {
    const { url } = req
    // 讀取支持的壓縮算法
    const acceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g)

    // 默認(rèn)響應(yīng)的數(shù)據(jù)類型
    res.setHeader('Content-Type', 'application/json; charset=utf-8')

    // 幾個(gè)示例的路由
    const routes = [
        ['/gzip', () => {
            if (acceptEncoding.includes('gzip')) {
                res.setHeader('content-encoding', 'gzip')
                // 使用同步API直接壓縮文本內(nèi)容
                res.end(zlib.gzipSync(Buffer.from(testTxt)))
                return
            }
            res.end(testTxt)
        }],
        ['/deflate', () => {
            if (acceptEncoding.includes('deflate')) {
                res.setHeader('content-encoding', 'deflate')
                // 基于流的單次操作
                const originStream = new PassThrough()
                originStream.write(Buffer.from(testTxt))
                originStream.pipe(zlib.createDeflate()).pipe(res)
                originStream.end()
                return
            }
            res.end(testTxt)
        }],
        ['/br', () => {
            if (acceptEncoding.includes('br')) {
                res.setHeader('content-encoding', 'br')
                res.setHeader('Content-Type', 'text/html; charset=utf-8')
                // 基于流的多次寫操作
                const originStream = new PassThrough()
                pipeline(originStream, zlib.createBrotliCompress(), res, (err) => {
                    if (err) {
                        console.error(err);
                    }
                })
                originStream.write(Buffer.from('<h2>BrotliCompress</h2>'))
                originStream.write(Buffer.from('<h3>測試數(shù)據(jù)</h3>'))
                originStream.write(Buffer.from(testTxt))
                originStream.end()
                return
            }
            res.end(testTxt)
        }]
    ]
    const route = routes.find(v => url.startsWith(v[0]))
    if (route) {
        route[1]()
        return
    }

    // 兜底
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end(`<h2>404: ${url}</h2>
    <h3>已注冊路由</h3>
    <ul>
        ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('')}
    </ul>
    `)
    res.end()
})

app.listen(3000)

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“如何利用Node實(shí)現(xiàn)內(nèi)容壓縮”這篇文章對大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI