http传输压缩 和 jszip压缩配置文件

一、压缩算法

1.zip

以下参考
他发明了 zip 压缩格式,功成名就之际死于堕落
压缩大战真相 转载自2004.10的《大众软件》,原作者为 广东 GZ
高压缩文件是如何实现的?
ZIP压缩算法详细分析及解压实例解释

其实LZ系列的算法并不新鲜,其中既没有高深的理论背景,也没有复杂的数学公式。它们只是简单的延续了千百年来人们对字典的追崇和喜好,并用一种极为巧妙的方式将字典技术运用于通用数据压缩领域。简单的说如果你习惯用字典中的页码和行号代替文章中的每个单词的时候,那实际上你已经掌握了LZ系列算法的真谛,因此这类编码算法被统称为Dictionary coders。

1952 年,麻省理工的一名学生 David A. Huffman 提出了 Huffman 编码,后作为一种无损压缩格式被广泛应用于数据压缩领域。

到了七八十年代,两名犹太人A.Lempel 和 J.Ziv 提出 LZ 字典编码算法。LZ是其发明者J.Ziv和A.Lempel两个犹太人姓氏的缩写。此二人于1977年发表题为《顺序数据压缩的一个通用算法》的论文,论文中描述的算法被后人称为LZ77算法。

1978年,二人又发表了该论文的续篇,描述了后来被命名为LZ78的压缩算法。

1984年,Welch这个人研究了LZ78算法的变种,因为是W在Z和L两人之后研究出来的,因此叫LZW算法。 LZW压缩算法是Unisys的专利,有效期到2003年,所以对它的使用已经没有限制了。

1985 年一家名为 SEA 的公司开发了 MS-DOS 环境下第一个应用 LZW 算法的 ARC 压缩软件,一时风头无两。

LZ77 并无专利限制,效果更佳的 LZ78 和 LZW 则有所限制,使用了专利算法的 ARC 自然是一款付费软件。也许是早早地觉醒了「互联网精神」之魂,不满 ARC 近乎垄断的状况,Katz 将 ARC 汇编重写为 PKARC 软件,完全兼容 ARC, 并且压缩速度更快,效果更好。

Katz 将 PKARC 以非强迫性注册的共享软件进行发放,并在一段时间后大大挤压了 ARC 的份额。SEA 公司原本希望 Katz 能将软件授权给他们,可惜洽谈无果,1988 年 SEA 将 Katz 告上法院,败诉的 Katz 依旧因不愿让 PKARC 成为商业软件而拒绝与 SEA 合作。

可以说是因祸得福,又或者是命中该历此劫一般,少年意气被彻底激发,Katz 立志要写出一种全新算法,大有不报此仇誓不为人的决绝与孤勇。

在放弃专利算法 LZW 和 LZ78 的情况下,Katz 日以夜继地刻苦攻克难关,希望能将 LZ77 和 Huffman 编码脱胎换骨,金手指大开的 Katz 几周之后就将两者完美结合,推出了后来被定义为 DEFLATE 的全新算法,以及建立于此算法基础上的 zip 压缩格式。

1989年,Katz 以其创立公司 PKWare 的名义推出 PKZIP,在压缩比、压缩率全面碾压 ARC 的情况下,少年心性进一步体现在 PKZIP 不兼容 ARC 并且将其作为自由软件发放,并在之后将 zip 算法公开,这个决定终结了数据无损压缩的垄断,真正造福了万千计算机用户。

1995 年,微软发布 Windows95,而与许多自由软件斗士相似, Katz 对微软并不属意,也就没有第一时间开发适合该系统的 zip 压缩软件,因为算法公开,另一家公司以 Winzip 趁虚而入,与 PKZIP 分庭抗礼,此后一度成为压缩软件的无冕之王,以至于至今仍有不少人认为 zip 为 Winzip 所发明。

2.gzip zlib
参考gzip,deflate,zlib辨析
GZIP, ZLIB, DEFLATE, 文件格式
Java压缩算法性能比较

deflate(RFC1951):一种压缩算法,使用LZ77和哈弗曼进行编码;
zlib(RFC1950):一种格式,是对deflate进行了简单的封装,他也是一个实现库(delphi中有zlib,zlibex)
gzip(RFC1952):一种格式,也是对deflate进行的封装。

gzip = gzip头 + deflate编码的实际内容 + gzip尾
zlib = zlib头 + deflate编码的实际内容 + zlib尾

GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet上使用非常普遍的一种数据压缩格式,或者说一种文件格式。HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。
GZIP本身只是一种文件格式,其内部通常采用DEFLATE数据格式,而DEFLATE采用LZ77压缩算法来压缩数据。

二、HTTP 传输内容的压缩

参考 HTTP 传输内容的压缩

HTTP压缩是指: Web服务器和浏览器之间压缩传输的”文本内容“的方法。 HTTP采用通用的压缩算法,比如gzip来压缩html,javascript, CSS文件。 能大大减少网络传输的数据量,提高了用户显示网页的速度。当然,同时会增加一点点服务器的开销。 本文从HTTP协议的角度,来理解HTTP压缩这个概念。

  1. 浏览器发送Http request 给Web服务器, request 中有Accept-Encoding: gzip, deflate。 (告诉服务器, 浏览器支持gzip压缩)

  2. Web服务器接到request后, 生成原始的Response, 其中有原始的Content-Type和Content-Length。

  3. Web服务器通过Gzip,来对Response进行编码, 编码后header中有Content-Type和Content-Length(压缩后的大小), 并且增加了Content-Encoding:gzip. 然后把Response发送给浏览器。

  4. 浏览器接到Response后,根据Content-Encoding:gzip来对Response 进行解码。 获取到原始response后, 然后显示出网页。


    image.png

HTTP定义了一些标准的内容编码类型,并允许用扩展的形式添加更多的编码。Content-Encoding header 就用这些标准化的代号来说明编码时使用的算法

Content-Encoding值

  • gzip  表明实体采用GNU zip编码
  • compress 表明实体采用Unix的文件压缩程序
  • deflate  表明实体是用zlib的格式压缩的
  • identity  表明没有对实体进行编码。当没有Content-Encoding header时, 就默认为这种情况
  • gzip, compress, 以及deflate编码都是无损压缩算法,用于减少传输报文的大小,不会导致信息损失。 其中gzip通常效率最高, 使用最为广泛。

压缩的好处

http压缩对纯文本可以压缩至原内容的40%, 从而节省了60%的数据传输。

Gzip的缺点

JPEG这类文件用gzip压缩的不够好。

Gzip是如何压缩的

简单来说, Gzip压缩是在一个文本文件中找出类似的字符串, 并临时替换他们,使整个文件变小。这种形式的压缩对Web来说非常适合, 因为HTML和CSS文件通常包含大量的重复的字符串,例如空格,标签。

参考
tomcat和nginx的gzip压缩
Tomcat 和 Nginx 开启Gzip功能的方法

三、node.js中的压缩和解压缩

1.以下参考Zlib模块实现流压缩与解压
在流传输过程中,为减少传输数据加快传输速度,往往会对流进行压缩。HTTP流就是如此,为提高网站响应速度,会在服务端进行压缩,客户端收到数据后再进行相应的解压。Node.js中的Zlib模块提供了流压缩与解压缩功能,Zlib模块提供了对 Gzip/Gunzip、Deflate/Inflate、DeflateRaw/InflateRaw类的绑定,这些类可以实现对可读流/可写流的压缩与解压。

deflate(RFC1951)是一种压缩算法,使用LZ77和哈弗曼进行编码。gzip(RFC1952)一种压缩格式,是对deflate的简单封装,gzip = gzip头(10字节) + deflate编码的实际内容 + gzip尾(8字节)。在HTTP传输中,gzip是一种常用的压缩算法,使用gzip压缩的HTTP数据流,会在HTTP头中使用Content-Encoding:gzip进行标识。

使用Zlib模块创建类
在Node.js的Zlib模块中,提供了对gzip和deflate压缩/解压类的创建方法。详细如下:

  • 创建gzip压缩类Gzip:zlib.createGzip([options])
  • 创建gzip解压类Gunzip:zlib.createGunzip([options])
  • 创建deflate压缩类Deflate:zlib.createDeflate([options])
  • 创建deflate解压类Inflate:zlib.createInflate([options])
  • 创建deflate原始数据压缩类DeflateRaw:zlib.createDeflateRaw([options])
  • 创建deflate原始数据解压类InflateRaw:zlib.createInflateRaw([options])

在使用这些方法创建类时,都有一个Options选项,可选各项如下:

  • flush(缺省:zlib.Z_NO_FLUSH)
  • chunkSize(缺省:16*1024)
  • windowBits
  • level(仅用于压缩)
  • memLevel(仅用于压缩)
  • strategy(仅用于压缩)
  • dictionary(仅用于 deflate/inflate,缺省为空目录)

Zlib模块中的方法
在Zlib模块中,有一些方法可以方便对缓存数据或字符串进行压缩或解压处理,这些方法第一个参数为字符串或缓存,第二个可选参数可以供其内部的zlib类使用。方法提供回调和同步两种模式,回调模式下回调函数为callback(error, result),*Sync结尾的为同步方法,它接收相同参数,但没有回调函数。

使用Deflate对字符串或缓存进行压缩:
zlib.deflate(buf[, options], callback)、zlib.deflateSync(buf[, options])
使用DeflateRaw对字符串或缓存进行压缩:
zlib.deflateRaw(buf[, options], callback)、zlib.deflateRawSync(buf[, options])
使用Gzip对字符串或缓存进行压缩:
zlib.gzip(buf[, options], callback)、zlib.gzipSync(buf[, options])
使用Gunzip对缓存进行解压:
zlib.gunzip(buf[, options], callback)、zlib.gunzipSync(buf[, options])
使用Inflate对缓存进行解压:
zlib.inflate(buf[, options], callback)、zlib.inflateSync(buf[, options])
使用InflateRaw对缓存进行解压:
zlib.inflateRaw(buf[, options], callback)、zlib.inflateRawSync(buf[, options])
使用Unzip对缓存进行压缩:
zlib.unzip(buf[, options], callback)、zlib.unzipSync(buf[, options])

使用Zlib模块,在HTTP客户端对数据进行解压缩示例:

var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
var request = http.get({ host: 'itbilu.com',
                         path: '/',
                         port: 80,
                         headers: { 'accept-encoding': 'gzip,deflate' }});
request.on('response', function(response) {
  var output = fs.createWriteStream('itbilu.com_index.html');
  switch (response.headers['content-encoding']) {
    // 可以使用zlib.createUnzip()处理两种情况
    case 'gzip':
      response.pipe(zlib.createGunzip()).pipe(output);
      break;
    case 'deflate':
      response.pipe(zlib.createInflate()).pipe(output);
      break;
    default:
      response.pipe(output);
      break;
  }
});

使用Zlib模块,创建一个HTTP服务端,根据客户端请求的编码类型,进行gzlip或deflate相应。示例如下:

var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
  var raw = fs.createReadStream('index.html');
  var acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  // 对客户端请求的 accept-encoding 进行简单解析(非标准的)
  if (acceptEncoding.match(/\bdeflate\b/)) {
    response.writeHead(200, { 'content-encoding': 'deflate' });
    raw.pipe(zlib.createDeflate()).pipe(response);
  } else if (acceptEncoding.match(/\bgzip\b/)) {
    response.writeHead(200, { 'content-encoding': 'gzip' });
    raw.pipe(zlib.createGzip()).pipe(response);
  } else {
    response.writeHead(200, {});
    raw.pipe(response);
  }
}).listen(1337);

2.以下参考Nodejs中的压缩和解压缩
nodejs自带的压缩解压缩模块"zlib",先来看一段官网提供的示例代码

var zlib = require('zlib'); 
var gzip = zlib.createGzip(); 
var fs = require('fs'); 
var inp = fs.createReadStream('input.txt'); 
var out = fs.createWriteStream('input.txt.gz'); 
inp.pipe(gzip).pipe(out); 

按示例试验,成功了,可是为什么我只能把一个文件打包啊,坑爹啊!!但是,这个模块的说明文档上还有着好多转码的东西,看起来更像是用来网页传输加密解密的东西!

3.以下参考nodejs中用zlib解压gzip
为了减少网络传输数据量,http传输过程中会采用通用的压缩算法来压缩数据,gzip属于最常用的压缩算法。

使用node的http模块发送请求时并没有帮我们进行解压,因此我们需要手动去判断gzip。

var http = require('http');
var options = {
 hostname: 'www.qq.com',
 port: 80,
 method: 'get',
 headers: {
  'Accept-Encoding': 'gzip'
 }
}

http.request(options, handler);

function handler(responder) {
 // do something
}

我们设置http头Accept-Encoding为gzip告诉服务器我们支持gzip压缩,服务器收到请求后,返回responder头中content-encoding带有gzip表明返回的数据为gzip压缩过的内容,node中可以通过responder.headers[‘content-encoding’]来判断服务器返回内容是否gzip压缩过。

function handler(responder) {
 if(responder.headers['content-encoding'].indexOf('gzip') != -1) {
  // 解压gzip
 }
}

已经可以判断服务器返回的是gzip压缩过的内容,接下来我们需要去解压,幸好node提供了zlib模块。我们用zlib模块来解压gzip

var zlib = require('zlib');
var fs = require('fs');
var gunzipStream = zlib.createGunzip();
var toWrite = fs.createWriteStream('qq.html');

//zlib.createGunzip是一个transform流,通过pipe我们可以很方便解压gzip
function handler(responder) {
 if(responder.headers['content-encoding'].indexOf('gzip') != -1) {
  responder.pipe(gunzipStream).pipe(toWrite);
 }
}
三、jszip

参考
github jszip
jszip官方教程
【咸鱼教程】JsZip压缩与解压教程
简单封装jszip的第三方扩展,希望对你有用
Egret 压缩与解压(jszip)
jszip demo

jszip 是一个 JavaScript 库,可直接在浏览器上创建 zip 压缩档。在中大型游戏,不可避免有大量的游戏配置文件(txt或json)。为了减少加载次数和传输量,我们事先将这些文件压缩打包成zip, 然后在游戏中加载,并使用jszip库解压获取其中的数据。

1.使用

//加载zip
RES.getResByUrl("resource/assets/assets.zip", function(data){
  //解压数据
  var zip = new JSZip(data);
  //读取技能数据
  var skillJson = JSON.parse(zip.file("skill.json").asText());
  console.log(skillJson);
},this, RES.ResourceItem.TYPE_BIN);

2.生成压缩文件
参考zip压缩工具jszip--nodejs常用模块(8)

var JSZip = require('jszip');
var fs = require('fs');
var zip = new JSZip();

zip.file('test.txt', 'hello there');
zip.folder("images/img").file("201.gif", fs.readFileSync("../../source/img/201.gif"));
zip.folder("images/img").file("199.jpg", fs.readFileSync("../../source/img/199.jpg"));
zip.folder("images/img").file("weibo.png", fs.readFileSync("../../source/img/weibo.png"));
zip.folder("images/js").file("navigation.js", fs.readFileSync("../../source/js/navigation.js"));
var data = zip.generate({base64:false,compression:'DEFLATE'});
fs.writeFile('demo.zip', data, 'binary', function(){
    console.log('success');
});

注:上面这段代码运行会提示方法已经过期,官网给出最新的方式是这样的:

       zip
         .generateNodeStream({
           type: 'nodebuffer',
           streamFiles: true
         })
         .pipe(fs.createWriteStream('out.zip'))
         .on('finish', function() {
           // JSZip generates a readable stream with a "end" event,
           // but is piped here in a writable stream which emits a "finish" event.
           console.log("out.zip written.");
         });

这个官方例子压缩出来的文件包挺大,因为使用了默认STORE方式。需要改成

         .generateNodeStream({
           type: 'nodebuffer',
           compression:'DEFLATE',
           streamFiles: true
         })

生成的demo.zip的目录结构:
demo.zip
|--test.txt
|--images
| |--img
| | |--201.gif
| | |--199.jpg
| | |--weibo.png
| |--js
| | |--navigation.js
参数说明
这个工具api很多,这里就不列出来,详细信息看官方文档吧。但在使用过程中,发现官方文档有个问题,这里记录下。
对于generate方法options参数中base64属性,默认值为true(官方文档说默认值为false);指定为true或不指定时,生成的zip包会有问题,后续使用这个api时记得加一下就好。

看看官方例子

var zip = new JSZip();
zip.file("Hello.txt","Hello World\n");
var img = zip.folder("images");
var imgData = "R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=";
img.file("smile.gif", imgData, {base64: true});
var content = zip.generate({type:"blob"});
saveAs(content,"examlpe.zip");

四、Laya中使用zlib.js(as3)

1.问题1
参考关于jszip库的使用问题,jszip对压缩文件可以解压读取。通讯过程中的压缩还是用zlib

2.问题2
以下参考Zlib is not defind?

image.png

zlib是一个外挂模式(文件:zlip.min.js),bytearray 的解压缩用的是第三方的类库zlib.min.js。譬如:AS项目中用到了ByteArray(ba).uncompress类似写法,翻译成js后报zlib文件未定义的错。修改此错的方法就是在你的html文件内添加script标签,对该js文件进行添加,具体如下:

<script src='zlib.min.js' loader='laya'></script>//你需要添加的第三方js文件的script标签
<script src='test100.max.js' loader='laya'></script>//自动生成的主js文件的script标签。

注意:你自己添加的第三方script标签放到主js文件的script标签之上,确保可以加载到。
这样你的报错就消失了。

end

推荐阅读更多精彩内容