JavaScript实现Gzip压缩

215
我正在编写一个Web应用程序,需要通过AJAX将JSON数据存储在小型且固定大小的服务器端缓存中(类似于Opensocial配额)。我无法控制服务器。
为了保持服务器端配额内存储的数据量,我需要减少存储数据的大小,并希望能够在将其发送到服务器之前在浏览器中对字符串化的JSON进行gzip压缩。
然而,我找不到太多JavaScript实现Gzip的方法。有没有建议可以在客户端上压缩数据并将其发送?

7
您正在将数据发送给服务器,这就是为什么有“上传”和“下载”的概念。也许这就是您得到的答案告诉您“服务器可以处理它”的原因。 - Tomalak
3
一个正确的实现可能会比较棘手,因为JavaScript是单线程的。它可能需要使用setTimeout()分批压缩,以便在压缩时不会锁定用户界面。 - August Lilleaas
也许你可以编写自己的压缩算法。 - Captain kurO
3
现在你可以使用Web Worker来完成这个任务。 :) - Captain Obvious
9个回答

149

编辑:有一个更好的 LZW 解决方案可以正确处理 Unicode 字符串,位于 http://pieroxy.net/blog/pages/lz-string/index.html(感谢评论中的 pieroxy)。


我不知道任何 gzip 实现,但是 jsolait 库(该网站似乎已经关闭)具有 LZW 压缩/解压缩函数。代码受 LGPL 许可证保护。

// LZW-compress a string
function lzw_encode(s) {
    var dict = {};
    var data = (s + "").split("");
    var out = [];
    var currChar;
    var phrase = data[0];
    var code = 256;
    for (var i=1; i<data.length; i++) {
        currChar=data[i];
        if (dict[phrase + currChar] != null) {
            phrase += currChar;
        }
        else {
            out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
            dict[phrase + currChar] = code;
            code++;
            phrase=currChar;
        }
    }
    out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
    for (var i=0; i<out.length; i++) {
        out[i] = String.fromCharCode(out[i]);
    }
    return out.join("");
}

// Decompress an LZW-encoded string
function lzw_decode(s) {
    var dict = {};
    var data = (s + "").split("");
    var currChar = data[0];
    var oldPhrase = currChar;
    var out = [currChar];
    var code = 256;
    var phrase;
    for (var i=1; i<data.length; i++) {
        var currCode = data[i].charCodeAt(0);
        if (currCode < 256) {
            phrase = data[i];
        }
        else {
           phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);
        }
        out.push(phrase);
        currChar = phrase.charAt(0);
        dict[code] = oldPhrase + currChar;
        code++;
        oldPhrase = phrase;
    }
    return out.join("");
}

11
根据维基百科,这些专利已经过期了几年。不过最好再确认一下。 - Matthew Crumley
3
LZW 已经太过古老,无法继续被专利保护。最后一个专利在 2003 年左右到期。有很多免费的实现版本可用。 - ypnos
5
上述代码存在至少两个问题:1)试图压缩“Test to compress this \u0110\u0111\u0112\u0113\u0114 non ascii characters.”,2)如果代码大于65535,则没有报告错误。 - some
6
这里有21种不同语言的实现 http://rosettacode.org/wiki/LZW_compression,据称这些内容在2004年已进入公共领域。 - jcubic
6
我刚发布了一个小型库,可以纠正你在这里指出的问题:http://pieroxy.net/blog/pages/lz-string/index.html。 - pieroxy
显示剩余14条评论

60

我有另外一个问题,我不想将数据编码为gzip格式,而是要解码gzip压缩的数据。因为我在浏览器之外运行JavaScript代码,所以我需要使用JavaScript进行解码。

我花了一些时间,但是在JSXGraph库中发现了一种读取gzipped数据的方法。

这里是我找到该库的地方:http://jsxgraph.uni-bayreuth.de/wp/2009/09/29/jsxcompressor-zlib-compressed-javascript-code/ 甚至还有一个独立的实用程序JSXCompressor可以做到这一点,并且代码是LGPL许可证。

只需在您的项目中包含jsxcompressor.js文件,然后您就能够读取基于base64编码的gzip压缩数据:

<!doctype html>
</head>
<title>Test gzip decompression page</title>
<script src="jsxcompressor.js"></script>
</head>
<body>
<script>
    document.write(JXG.decompress('<?php 
        echo base64_encode(gzencode("Try not. Do, or do not. There is no try.")); 
    ?>'));
</script>
</html>

我知道这不是你想要的,但我还是在这里回答,因为我认为它对某些人会有帮助。


3
非常感谢您的分享。这正是我所需要的。你可能为我节省了几个小时的无功搜索,而这些时间我实在无法浪费。+1 - Kiruse
1
我不知道为什么它被称为“压缩器”,而它实际上是个解压器。哈哈 - matteo
1
五年过去了,仍然非常有用。谢谢。我直接将一个大的JSON转储到页面上,而不是通过AJAX传输。通过使用PHP进行预压缩并在JavaScript客户端端解压缩,我节省了一些开销。 - user257319
我们需要 <?php.. 这一部分吗?我问这个问题是因为它被传递给了 decompress 方法。 - Jus12
我得到了 14:16:28.512 TypeError: e.replace 不是一个函数[更多信息] jsxcompressor.min.js:19:12201 - Bluscream
谢谢!这真的帮了我,我已经寻找解决方案两天了!再次感谢您... - Yohannes Kristiawan

43

我们刚刚发布了pakohttps://github.com/nodeca/pako,这是将zlib移植到JavaScript的版本。我认为它现在是最快的JS实现压缩/解压缩/gzip/ungzip。另外,它采用民主的MIT许可证。Pako支持所有zlib选项,且其结果是二进制相等的。

例如:

var inflate = require('pako/lib/inflate').inflate; 
var text = inflate(zipped, {to: 'string'});

7
请提供一个客户端的示例来解码经过gzip压缩的字符串。 - Redsandro
2
var inflate = require('pako/lib/inflate').inflate; var text = inflate(zipped, {to: 'string'}); @Redsandro 这是我使用pako的方法。 - forresto
该客户端示例抛出“不正确的标头检查”错误。 - duhaime

17

我将一个基于GWT模块的LZMA实现移植到了独立的JavaScript中,称其为LZMA-JS.


1
你有相容的 PHP 模組嗎? - Sirber

14

这个LZMA实现需要BrowserPlus(一个浏览器扩展程序),并且似乎不是纯JavaScript。 - Piotr Findeisen
这个LZ77实现已经不再可用,而且它的Python版本(发布在同一页上)对于相当简单的输入也是错误的。 - Piotr Findeisen
Geocities已经关闭,将会更新链接。 - Mauricio Scheffer
这非常接近我想要的。搜索一些东西后,我也会在这里更新。 - Theofanis Pantelides

9

1
这是zip格式,而不是gzip格式,并且它在底层使用pako。区别在于zip具有文件信息元数据。 - Vitaly

0

我猜一种通用的客户端JavaScript压缩实现在处理时间方面会非常昂贵,而不是通过传输几个未压缩有效载荷的HTTP数据包所需的传输时间。

你有做过任何测试吗?这样可以让你了解有多少时间可以节省。我的意思是,带宽节省可能不是你追求的目标,或者说它是吗?


我需要将总数据大小保持在一定的配额内--大小比时间更重要。 - David Citron
嗯... 为什么有限制?只是好奇。 - Tomalak
这是谷歌对此的看法:http://code.google.com/apis/opensocial/articles/persistence-0.8.html#restrictions-quotas -- 典型的Opensocial配额约为10K。 - David Citron
1
根据压缩的强度,您可以使用Web Workers在幕后执行任务。 - zachleat

-6
大多数浏览器都可以即时解压gzip。 这可能比javascript实现更好的选择。

24
好的,但在发送数据之前,我需要在客户端对数据进行压缩处理... - David Citron

-6

您可以在页面中嵌入一个每像素1个像素的Java小程序,并将其用于压缩。

它不是JavaScript,客户端需要Java运行时,但它可以满足您的需求。


7
有趣,但如果可能的话我宁愿避免包含小程序。 - David Citron
我想添加真实的使用案例。 - cmc
3
添加 Java 依赖项不是一个好的解决方案。此外,并非每个人都愿意安装 Java,因此该网站对于某些人来说无法使用。个人而言,我之前因为某个需求安装了 Java,但我更喜欢访问不使用 Java 的网站。 - Onkelborg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接