gzip压缩简介要进行压缩,首先使用LZ77算法进行压缩,然后对得到的结果进行霍夫曼编码。根据实际情况判断是使用动态霍夫曼编码还是静态霍夫曼编码,最后生成对应的gz压缩文件。您可能已经注意到gzip和deflate之间的区别。在我们的HTTP请求头中,会有一个accept-encoding字段。其实就是告诉服务器客户端可以接受的文件压缩格式,包括gzip、deflate和br。.那么常见的gzip和deflate有什么区别呢?1.GZIP,似乎是一个不透明或原子函数。事实上,HTTP定义了一种机制,Web客户端和Web服务器可以通过该机制就可用于发送内容的压缩方案达成一致。这是使用Accept-Encoding和Content-Encoding标头完成的。有两种常用的HTTP压缩:DEFLATE和GZIP。2、DEFLATE是一种无专利的压缩算法,可以实现无损数据压缩,开源的实现算法很多。大多数人使用的标准实现库是zlib。zlib库提供使用DEFLATE/INFLATE压缩和解压缩数据。zlib库还提供了一种数据格式,混淆名称为ZLIB,它包装了DEFLATE压缩数据,带有标头和校验和。总而言之,GZIP是另一个使用DEFLATE来压缩数据的压缩库。事实上,大多数GZIP实现实际上使用zlib库的内部来进行DEFLATE/INFLATE压缩操作。GZIP产生自己的数据格式,容易混淆地命名为GZIP,它包装了DEFLATE压缩数据,带有标头和校验和。由于原有规定的不一致,大部分情况下都启用了deflate压缩。gzip压缩的原理接下来,我们就进入本文的一个正题。gzip压缩的过程是怎样的?其实gzip压缩一般都是针对文本文件进行压缩的。至于原因,后面会介绍。LZ77算法首先基于文本文件压缩文本内容,即文件中的字符串。接下来使用哈夫曼编码,转换成010111..等二进制进行存储。那么具体的算法是什么呢?1.LZ77算法LZ77算法的核心思想是复用字符串。在扫描整个文本的过程中,判断前面的字符串中是否存在相似的字符串或子串。这样可以压缩你的文字长度,举个简单的栗子:文字内容如下:http://www.qq.comhttp://www.paipai.com我们附上同样的内容qq.com(http://www.)paipai(.com)被信息对替换,结果为http://www.qq.com(18,11)paipai(22,4)其中(18,11),18(从起点到下一个起点,包括换行符)是同一内容块到当前位置的距离,11是同一内容的长度。在(22,4)中,22是同一个内容块到当前位置的距离,4是同一个内容的长度。由于消息对的大小小于替换内容的大小,因此文件被压缩。对于算法的具体实现,首先需要明确几个名词概念:1.forwardbuffer每次读取数据时,先将一部分数据预加载到forwardbuffer中。准备移入滑动窗口2.滑动窗口数据一旦通过缓冲区,就会被移入滑动窗口,成为字典的一部分。3.词组词典由字符序列S1...Sn中的n个词组组成。例如字符(A,B,D),可以组合的词组有{(A),(A,B),(A,B,D),(B),(B,D),(D)},如果滑动窗口中的这些字符可以记录为当前词组词典,因为滑动窗口是不断向前滑动的,所以词组词典也在不断变化。算法在遍历整个文本字符串的过程中,通过判断前向缓冲区中滑动窗口是否存在最长子串,实现了字符串的“复用”,如下图所示:现有字符串,除单个字符外,有AB,BD,ABD,在forwardbuffer中,有AB,ABC,BC,最长子串是AB,那么ABC可以压缩成(0,2,C),其中0表示滑动窗口中的偏移量,2代表读取两个字符,C代表一个不匹配的字符。下面我们来看一个完整的压缩案例,你就明白了:(图片引用自:https://www.jb51.net/article/...)那么解压过程呢?其实解压的过程是很快的,如下:可以看出LZ77算法的耗时主要在压缩阶段,判断forwardbuffer和slidingwindow的最长字符串匹配,即选择滑动窗口的大小和前向缓冲区的大小对LZ77算法的时间复杂度有关键影响。其次,合适的滑动窗口大小和缓冲区大小可以帮助你更好地压缩文件,提高压缩率。在整个解压过程中,只需要读取简单的字符,根据偏移量对子串进行复用即可完成解压过程。整个过程没有复杂的算法和耗时,也符合我们的网络要求。在适用场景下,文件在服务器端压缩后提供给所有用户,数据在客户端解压,解压基本不花时间,所以可以大大提高我们页面资源的加载速度。2.哈夫曼编码当你对文件中的文本进行压缩后,下一步实际上就是文本的存储。在ASCII编码中,一个字符对应一个字节,用8位来表示,但是我们真的可以不优化吗??霍夫曼编码告诉我们可以压缩更多!huffman的原理是判断字符在文本中出现的频率,并指定每个字符的编码,而不是固定的8位编码。举个简单的栗子:string:AAABCB按照前面的存储方式需要6个字节,48bits。但是我们可以看到A出现了3次,B出现了2次,C只出现了1次,所以我们改变存储方式,用0代表A,10代表B,11代表C的最后一次压缩结果是000101110,最后我们只用了9位来存储这个字符串!这就是霍夫曼的基本原理。可以看出,我们的关键是为字符生成相应的代码,所以这个过程需要我们构造一棵哈夫曼树。什么是霍夫曼树?哈夫曼树使用一系列的值,并将这些值作为叶节点。请注意,叶节点被构造成一棵树。这棵树要求(叶子节点的权重和叶子节点的深度)所有叶子节点的总数n的值达到最小。具体构建过程如下:(图片引用自http://www.360doc.com/content...)其中,节点值表示字符出现的频率,叶子的层级节点变相表示字符代码的长度。其实这也很好理解。我们希望出现的字符越多,代码越短越好,出现的字符越少,代码长度越长无所谓。这也完全符合霍夫曼树的定义。可以看到字符串:abbbbccccdddeabcde14431经过我们的哈夫曼编码后,它们表示如下:a是110b是00c是01d是10e是111你可能已经注意到,经过哈夫曼编码后的二进制字符,是没有歧义的。可以看到在我们上面的例子中,哈夫曼码是动态生成的,所以最后传输数据的时候,需要把哈夫曼树的信息一起传递。当然,静态霍夫曼和动态霍夫曼还是有区别的:静态霍夫曼代码是使用gzip预先定义了一套代码进行压缩,解压的时候也使用这套代码,这样就不需要通过用于生成树的信息。动态哈夫曼编码是利用统计的每个符号出现的次数来构建哈夫曼树,生成每个符号的哈夫曼码,并用生成的哈夫曼码进行压缩,这样就需要传递生成树的信息。Gzip在对一个块进行哈夫曼编码之前,会同时建立静态哈夫曼树和动态哈夫曼树,然后根据要输出的内容和生成的内容,使用静态哈夫曼树编码计算生成块的大小哈夫曼树,并使用动态哈夫曼树编码计算生成块的大小。然后为了比较,使用生成更小块的方法进行霍夫曼编码。对于静态树,不需要传递用于生成树的部分信息。动态树需要传达这些信息。而当文件比较小的时候,传递生成树信息是得不偿失的,反而会让压缩后的文件变大。也就是说,当文件比较小时,可能会出现使用静态霍夫曼编码可能比动态霍夫曼编码生成更小的块。如何打开gzip?gzip这么好,赶紧在我们的项目中使用吧!express和koa都默认不开启gzip压缩,所以需要引入中间件开启压缩,对文本文件进行文本压缩。constKoa=require("koa");constpath=require("path")varcompress=require('koa-compress')constapp=newKoa();conststatic=require('koa-static')app.use(compress({filter:function(content_type){return/text/g.test(content_type)},threshold:1,flush:require('zlib').Z_SYNC_FLUSH}))app.use(async(ctx,next)=>{//ctx表示响应ctx.compress=trus表示允许的压缩//ctx.compress=true//ctx.body="hello"awaitnext()})app.use(static(path.join(__dirname,'public'),{gzip:true}))//可以配置一个或多个//app.use(static(__dirname+'/static')))app.listen(3000);需要注意koa中间件的使用和express不一样。二、使用filter函数判断是否进行gzip压缩。除了nginx或服务器层做gzip压缩。也可以在构建时压缩生成相应的gz文件。constCompressionWebpackPlugin=require('compression-webpack-plugin');webpackConfig.plugins.push(newCompressionWebpackPlugin({asset:'[path].gz[query]',algorithm:'gzip',test:newRegExp('\\.(js|css)$'),threshold:10240,minRatio:0.8}))关于gzip的问答:gzip是否仅限于文本文件,二进制文件呢?声音的?图片和其他资源呢?A:gzip压缩不仅适用于文本文件,它也可以压缩图片等二进制文件,但不推荐这样做,因为gzip压缩过程是先根据字符串进行LZ77处理,然后再经过huffman编码。然而,大多数二进制文件已经有了自己的编码和压缩方法。压缩二进制文件可能会产生更大的文件。同时会增加客户端的解压压力,造成不必要的CPU消耗。一些开发者使用HTTP对本地已经压缩过的文件进行压缩,当这些压缩后的文件再次被GZip压缩时,性能并没有得到提升,具体体现在以下两个方面。首先,HTTP压缩是有代价的。Web服务器获取它需要的内容,对其进行压缩,然后将其发送给客户端。如果无法进一步压缩内容,您只是在浪费CPU执行无意义的任务。其次,对已经过度压缩的内容进行HTTP压缩不会使其变小。事实上,添加标头、压缩字典和验证响应体实际上会使它变大,如下图所示:Q:gzip压缩文件可以再次压缩吗?多少次?A:可以重新压缩,但是第一次gzip压缩的文件已经是二进制文件了。重新压缩文件只会带来不必要的性能损失,还可能增加文件的大小。Q:是否需要对所有文本内容进行GZIP压缩?答:答案是否定的。你可以看到在我们的压缩过程中,我们需要执行算法。如果传输的字符串只有几十个字符,那么完全没有必要执行算法,对太短的字符进行压缩,反而会导致传输数据量的增加,例如,它可能有必要传输动态构建的霍夫曼树信息等。Q:在什么阶段对文件进行gzip压缩比较合适?A:在本地构建部署时,生成最终的gz压缩文件,通过webpack部署到服务器上是最高效的。如果引入中间件,在请求到来时压缩文本文件,只会增加用户的等待时间。比较长,所以笔者认为在webpack打包构建的时候这样做是最合理的。最后谢谢大家的观看!本期到此结束~下期见~
