在JavaScript中解压gzip和zlib字符串

41

我想从tmx文件中获取压缩层数据。谁知道JavaScript中用于解压gzip和zlib字符串的库?我尝试过zlib,但它对我没有用。例如,tmx文件中的图层数据为:

  <data encoding="base64" compression="zlib">
       eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
  </data>

我的 JavaScript 代码是

var base64Data = "eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==";
var compressData = atob(base64Data);
var inflate = new Zlib.Inflate(compressData);
var output = inflate.decompress();

它运行时显示错误消息"不支持的压缩方法"。但是我尝试使用在线工具http://i-tools.org/gzip进行解压,它返回了正确的字符串。


感谢您的提问(即使将压缩数据编码为base64似乎有点反常,因为base64是一种负压缩格式;我可以看到应用场景...) - Michael Scott Asato Cuthbert
4个回答

55

Pako是一个完整且现代的Zlib端口。

这里有一个非常简单的示例,您可以从中开始工作。

获取pako.js,您可以像这样解压缩byteArray:

<html>
<head>
  <title>Gunzipping binary gzipped string</title>
  <script type="text/javascript" src="pako.js"></script>
  <script type="text/javascript">

    // Get datastream as Array, for example:
    var charData    = [31,139,8,0,0,0,0,0,0,3,5,193,219,13,0,16,16,4,192,86,214,151,102,52,33,110,35,66,108,226,60,218,55,147,164,238,24,173,19,143,241,18,85,27,58,203,57,46,29,25,198,34,163,193,247,106,179,134,15,50,167,173,148,48,0,0,0];

    // Turn number array into byte-array
    var binData     = new Uint8Array(charData);

    // Pako magic
    var data        = pako.inflate(binData);

    // Convert gunzipped byteArray back to ascii string:
    var strData     = String.fromCharCode.apply(null, new Uint16Array(data));

    // Output to console
    console.log(strData);

  </script>
</head>
<body>
    Open up the developer console.
</body>
</html>

运行示例:http://jsfiddle.net/9yH7M/

或者,您可以在将数组发送为JSON或XML时对其进行base64编码,以减少开销。解码同理:

// Get some base64 encoded binary data from the server. Imagine we got this:
var b64Data     = 'H4sIAAAAAAAAAwXB2w0AEBAEwFbWl2Y0IW4jQmziPNo3k6TuGK0Tj/ESVRs6yzkuHRnGIqPB92qzhg8yp62UMAAAAA==';

// Decode base64 (convert ascii to binary)
var strData     = atob(b64Data);

// Convert binary string to character-number array
var charData    = strData.split('').map(function(x){return x.charCodeAt(0);});

// Turn number array into byte-array
var binData     = new Uint8Array(charData);

// Pako magic
var data        = pako.inflate(binData);

// Convert gunzipped byteArray back to ascii string:
var strData     = String.fromCharCode.apply(null, new Uint16Array(data));

// Output to console
console.log(strData);

运行示例: http://jsfiddle.net/9yH7M/1/

若要进一步了解,这里是pako API文档http://nodeca.github.io/pako/


HTML示例可能无法正常工作,因为“pako”未定义。API可能已更改,现在使用“require”。 - Quark
4
注意:String.fromCharCode.apply(null, new Uint16Array(data)) 对于较长的输入数据会失败(JavaScript 报错 Maximum call stack size exceeded)。这个 Stack Overflow 上的回答可以解决较长数组的问题:https://dev59.com/fWw05IYBdhLWcg3w41wp#20604561。 - Patrick
6
可以使用以下方式将长字符串转换为文本:var string = new TextDecoder("utf-8").decode(data),参考链接: https://dev59.com/h2ox5IYBdhLWcg3w1Xy-。 - Kamil Slowikowski
1
我遇到了一个问题。当我使用pako进行解压时,英文正常,但是遇到中文时,中文字符串会变成无法读取的代码。我不知道为什么。gzip压缩会删除字符集信息吗? - alan9uo

15

我可以通过zlib解决我的问题。我将代码修复如下:

var base64Data = "eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==";
var compressData = atob(base64Data);
var compressData = compressData.split('').map(function(e) {
    return e.charCodeAt(0);
});
var inflate = new Zlib.Inflate(compressData);
var output = inflate.decompress();

我有一个zip文件,它以普通的JavaScript字符串形式通过网络URL检索 - 我该如何解压缩它,以便使用其原始的“text-plain”内容? - Bekim Bacaj

10
对于任何使用Ruby on Rails的人,想要将压缩编码数据发送到浏览器,然后通过浏览器上的Javascript解压缩它,我已经将上述两个优秀的答案结合起来,形成了以下解决方案。这是我的应用控制器中的Rails服务器代码,它在将一个字符串发送到浏览器之前,对其进行压缩和编码,并将其作为@variable传递给.html.erb文件:
require 'zlib'
require 'base64'

    def compressor (some_string)
        Base64.encode64(Zlib::Deflate.deflate(some_string))
    end

这里是使用pako.min.js的Javascript函数:

function uncompress(input_field){
    base64data = document.getElementById(input_field).innerText;
    compressData = atob(base64data);
    compressData = compressData.split('').map(function(e) {
        return e.charCodeAt(0);
    });
    binData = new Uint8Array(compressData);
    data = pako.inflate(binData);
    return String.fromCharCode.apply(null, new Uint16Array(data));
}

以下是将存储在隐藏HTML字段中的数据进行解码和解压缩的javascript调用示例:
my_answer = uncompress('my_hidden_field');

以下是在Rails的 application.js 文件中调用位于/vendor/assets/javascripts目录下pako.min.js的入口代码:
//= require pako.min

我从这里获取了pako.min.js文件:

https://github.com/nodeca/pako/tree/master/dist

在我的端上一切都正常! :-)


0
我正在从Python脚本发送数据并尝试在JS中解码它。这是我所做的:
Python
import base64
import json
import urllib.parse
import zlib

...

data_object = {
    '_id': '_id',
    ...
}
compressed_details = base64.b64encode(zlib.compress(bytes(json.dumps(data_object), 'utf-8'))).decode("ascii")
urlsafe_object = urllib.parse.quote(str(compressed_details))#.replace('%', '\%') # you likely don't need this last part
final_URL = f'https://my.domain.com?data_object={urlsafe_object}'

...

JS

// npm install this
import pako from 'pako';

...

const urlParams = new URLSearchParams(window.location.search);
const data_object = urlParams.get('data_object');
if (data_object) {
    const compressedData = Uint8Array.from(window.atob(data_object), (c) => c.charCodeAt(0));
    originalObject = JSON.parse(pako.inflate(compressedData, { to: 'string' }));
};

...

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