Skip to content
微信公众号

压缩构建的数据资源

数据压缩是提高 Web 站点性能的一种重要手段。对于有些文件来说,高达 70% 的压缩比率可以大大减低对于带宽的需求。随着时间的推移,压缩算法的效率也越来越高,同时也有新的压缩算法被发明出来,应用在客户端与服务器端。

HTTP 响应数据压缩

压缩 JS、CSS

这里所说的压缩指的是去除换行空格之类的压缩,文件内容不变。

使用 Gzip 压缩文本

浏览器和服务器之间会使用主动协商机制。浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级,服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应中, Vary 首部中至少要包含 Accept-Encoding ;这样的话,缓存服务器就可以对资源的不同展现形式进行缓存。

下面是一个请求响应的 HTTP 报文示例:

js
GET /encrypted-area HTTP/1.1 
Host: www.example.com 
Accept-Encoding: gzip, deflate
js
HTTP/1.1 200 OK 
Date: Tue, 27 Feb 2018 06:03:16 GMT 
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) 
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT 
Accept-Ranges: bytes 
Content-Length: 438 
Connection: close 
Content-Type: text/html; charset=UTF-8 
Content-Encoding: gzip

压缩图片

在图片优化章节中详细说明。

HTTP 请求数据压缩

头部数据压缩

HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 Cookie 和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用 gzip 或 compress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

请求体数据压缩

前面我们介绍了 HTTP 协议中的 Accept-Encoding/Content-Encoding 机制。这套机制可以很好地用于文本类响应正文的压缩,可以大幅减少网络传输,从而一直被广泛使用。但 HTTP 请求的发起方(例如浏览器),无法事先知晓要访问的服务端是否支持解压,所以现阶段的浏览器没有压缩请求正文。

有一些通讯协议基于 HTTP 做了扩展,他们的客户端和服务端是专用的,可以放心大胆地压缩请求正文。例如 WebDAV 客户端就是这样。

实际的 Web 项目中,会存在请求正文非常大的场景,例如发表长篇博客,上报用于调试的网络数据等等。这些数据如果能在本地压缩后再提交,就可以节省网络流量、减少传输时间。本文介绍如何对 HTTP 请求正文进行压缩,包含如何在服务端解压、如何在客户端压缩两个部分。

开始之前,先来介绍本文涉及的三种数据压缩格式:

  • DEFLATE,是一种使用 Lempel-Ziv 压缩算法(LZ77)和哈夫曼编码的压缩格式。详见 RFC 1951
  • ZLIB,是一种使用 DEFLATE 的压缩格式,对应 HTTP 中的 Content-Encoding: deflate。详见 RFC 1950
  • GZIP,也是一种使用 DEFLATE 的压缩格式,对应 HTTP 中的 Content-Encoding: gzip。详见 RFC 1952

Content-Encoding 中的 deflate,实际上是 ZLIB。为了清晰,本文将 DEFLATE 称之为 RAW DEFLATE,ZLIB 和 GZIP 都是 RAW DEFLATE 的不同 Wrapper。

下面是一个简单示例。

  1. 压缩请求正文数据
js
var rawBody = 'content=test'; 
var rawLen = rawBody.length; 
var bufBody = new Uint8Array(rawLen); 
for(var i = 0; i < rawLen; i++) { 
    bufBody[i] = rawBody.charCodeAt(i); 
} 
var format = 'gzip'; // gzip | deflate | deflate-raw 
var buf; 
switch(format) { 
    case 'gzip': 
        buf = window.pako.gzip(bufBody); 
        break; 
    case 'deflate': 
        buf = window.pako.deflate(bufBody); 
        break; 
    case 'deflate-raw': 
        buf = window.pako.deflateRaw(bufBody); 
        break; 
} 
var xhr = new XMLHttpRequest(); 
xhr.open('POST', '/node/'); 
xhr.setRequestHeader('Content-Encoding', format); 
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
xhr.send(buf);

在 Node 中解压请求正文中的数据

js
var http = require('http'); 
var zlib = require('zlib'); 
http.createServer(function (req, res) { 
    var zlibStream; 
    var encoding = req.headers['content-encoding']; 
    switch(encoding) { 
        case 'gzip': 
        zlibStream = zlib.createGunzip(); 
        break; 
        case 'deflate': 
        zlibStream = zlib.createInflate(); 
        break; 
        case 'deflate-raw': 
        zlibStream = zlib.createInflateRaw(); 
        break;
    } 
res.writeHead(200, {'Content-Type': 'text/plain'}); 
req.pipe(zlibStream).pipe(res); }).listen(8361, '127.0.0.1');

实际使用还需要匹配具体的服务器,比如 nginx、Apache 等。

参考链接

本站总访问量次,本站总访客数人次
Released under the MIT License.