【问题标题】:JavaScript Zlib DecompressJavaScript Zlib 解压
【发布时间】:2014-10-30 03:34:08
【问题描述】:

我正在尝试解压缩 zlib'ed XML,如下所示: https://drive.google.com/file/d/0B52P0MZLTdw8ZzQwQzVpZGZVZWc

上传到在线解压服务作品,如:http://i-tools.org/gzip

在 PHP 中,我正在使用这段代码并且工作得很好,我得到了 XML 字符串:

$raw = file_get_contents("file_here");
$uncompressed = zlib_decode($raw);

但是,我想在 JavaScript 中执行此操作。

  • 该应用是一个客户端 Chrome 扩展程序,它使用从网络日志中读取的chrome.devtools.network
  • 读取二进制响应。顶部的 Google 云端硬盘链接示例
  • JS 需要将该响应解压为原始 XML,然后解析为对象

我唯一的问题是 zlib 解压缩部分。

从最新更新开始,解压库可以工作,但解压却不能。请跳至底部的 9 月 16 日更新。


我已经尝试了几个 JavaScript 库,但仍然无法正常工作:

帕科https://github.com/nodeca/pako

unpack() 代码:https://codereview.stackexchange.com/questions/3569/pack-and-unpack-bytes-to-strings

function unpack(str) {
    var bytes = [];
    for(var i = 0, n = str.length; i < n; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char >>> 8, char & 0xFF);
    }
    return bytes;
}

$.get("file_here", function(response){
    var charData    = unpack(response);
    var binData     = new Uint8Array(charData);
    var data        = pako.inflate(binData);
    var strData     = String.fromCharCode.apply(null, new Uint16Array(data));
    console.log(strData);
});

错误:Uncaught incorrect header check

即使将响应放在其他地方也是一样的:

  • new Uint8Array(response);
  • pako.inflate(response);

Imaya 的 zlibhttps://github.com/imaya/zlib.js

$.get("file_here", function(response){
    var inflate = new Zlib.Inflate(response);
    var output = inflate.decompress();
    console.log(output);
});

错误:Uncaught Error: unsupported compression methodinflate.js:60

仍然使用 Imaya 的 zlib,结合这个 Stack Overflow 问题: Decompress gzip and zlib string in javascript

$.get("file_here", function(response){
    var response = response.split('').map(function(e) {
        return e.charCodeAt(0);
    });
    var inflate = new Zlib.Inflate(response);
    var output = inflate.decompress();
    console.log(output);
});

错误:Uncaught Error: invalid fcheck flag:29inflate.js:65


dankogai 的 js-deflatehttps://github.com/dankogai/js-deflate

console.log(RawDeflate.inflate(response));

输出:


augustl 的 js-inflatehttps://github.com/augustl/js-inflate

console.log(JSInflate.inflate(response));

输出:


zlib-browserifyhttps://github.com/brianloveswords/zlib-browserify

错误:ReferenceError: exports is not defined

这只是 Imaya 的 zlib 的包装器。我想这是requireJS?我什至不确定如何使用它。它甚至可以在不安装任何东西而只安装 jQuery/JS 的情况下使用吗?上面提到的应用程序是可下载的 Chrome 扩展程序,只有 HTML 导入 JS 文件。


2014 年 9 月 16 日更新

看来问题出在 JavaScript unpack( ) 函数上。当我使用 PHP 生成的 ByteArray:http://pastebin.com/uDWvK94B 时,JavaScript 解压函数起作用。

有效的 PHP 解包:

$unpacked = unpack("C*", $raw);

对于我使用的 JavaScript unpack( ) 代码,它不起作用,请参阅 Pako 部分下的帖子顶部。

所以新的问题是,为什么 JavaScript 生成的 ByteArray 值与 PHP 生成的不同。

  • 真的是unpack( )函数有问题吗?
  • 还是当 JS 获取文件、编码或任何更改导致字节混乱时?
  • 最后,您建议的解决方法是什么?

2014 年 9 月 20 日更新

随着更多的研究和这里的一些答案给出线索

  • Sebastian S 提出问题出在检索数据的方式上,并且与文本编码有关
  • user3995789 提供了一个示例,即使在没有 unpack( ) 函数的情况下,它也可以工作,尽管在 Chrome 扩展程序的上下文之外
  • Isaac 在 Chrome 扩展程序的上下文中提供示例,但仍然不起作用

我研究了进一步结合所有线索,这使我得出一个理论,即这一切背后的原因是 Chrome 无法通过其request.getContent 函数获取“原始”数据。 See here 用于上述功能的 Chrome 文档。

到目前为止,我已将问题提交给 Chrome,see here

2015 年 3 月 24 日更新

虽然问题没有完全解决,但我认为对我最有用的答案来自@Sebastian S,他提出我获取或接收数据的“方式”有问题,转换错误是原因,与问题所在的位置一样接近。

【问题讨论】:

  • 你在用 chrome devtools 的扩展吗?
  • 是的,我在使用 chrome.devtools.network.onRequestFinished 的开发工具上,稍后将执行 request.getContent(function(response){,其中 response 是我需要解压缩的工具

标签: javascript jquery google-chrome-extension bytearray zlib


【解决方案1】:

我了解到您想在 chrome 扩展中使用 zlib 解压缩,同时从网络日志中读取响应正文。

你需要先获取要解压的base64。您可以在使用getContent 方法时实现这一点。

function zlibDecompress(base64Content){
    // var base64Content        = base64Content.split(',')[1]; // Not sure if need to keep it

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

    // 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 inflate
    var data        = pako.inflate(binData, { to: 'string' });

    return data;

}

chrome.devtools.network.onRequestFinished.addListener(
    function(request) {
        request.getContent(
            function(content, encoding){
                if(encoding == 'base64'){
                    var output = zlibDecompress(content);
                }
            }
        );
    }
);

https://developer.chrome.com/extensions/devtools_network#type-Request

使用 XMLHttpRequest :

<script type="text/javascript" src="pako.js"></script>
<script type="text/javascript">

function zlibDecompress(url){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';

    xhr.onload = function(oEvent) {
        // Base64 encode
        var reader = new window.FileReader();
        reader.readAsDataURL(xhr.response); 
        reader.onloadend = function() {
            base64data      = reader.result;  
            var base64      = base64data.split(',')[1];

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

            // 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 inflate
            var data        = pako.inflate(binData, { to: 'string' });

            console.log(data);
        }
    };

    xhr.send();
}
zlibDecompress('fileurl');
</script>

如果你想使用带有 chrome 扩展的 XMLHttpRequest

{
  "name": "My extension",
  ...
  "permissions": [
    "http://www.domain.com/", // The domain that hold the file
    "http://*/" // Or every domain
  ],
  ...
}

https://developer.chrome.com/extensions/xhr

如果您有任何问题,请随时提问;)

【讨论】:

  • 谢谢,我看到你一直在编辑你的答案。我从网络日志中得到的响应是二进制数据,如谷歌驱动器链接而不是 base64 所示。我现在最接近正确的 Uint8Array 是 TextEncoding 但仍将 unicode 字符解释为 3 个字节
  • 你用什么通过你的 chrome 扩展来捕获二进制文件?
  • @dragonjet 在用 `var strData = base64Content;` 替换 `var strData = atob(base64Content);` 时是否尝试过我的代码?
  • 我使用了onRequestFinished的监听器,它返回一个请求对象,可以用于request.getContent(function(response){})。从那里,response 是二进制数据。
  • 感谢艾萨克,这对我有帮助!
【解决方案2】:

jquery 读取utf8 格式,你必须读取原始文件,这个功能可以工作。

function readTextFile(file)
{
    var rawFile = new XMLHttpRequest();
    rawFile.open('GET', file, true);  
    rawFile.responseType = 'arraybuffer';
    rawFile.onload = function (response)
    {
      var words = new Uint8Array(rawFile.response);
       console.log(words[1]);
      console.log(pako.ungzip(words));

    };
    rawFile.send();
}

有关更多信息,请参阅此answer

【讨论】:

  • 这可以正常工作,但不能在 Chrome 扩展环境中使用,因为我们从网络日志中读取响应正文。有没有办法在不做 XHR 的情况下将正确的 ByteArray 值存储到 JavaScript 变量中?
  • 什么意思,$.get 也是做XHR的,你是怎么读取Chrome扩展里面的文件的,那是什么环境?无论如何,我发现了你的问题,请接受并给予赏金。 @dragonjet
  • 嗯。我得到一个巨大的数组作为返回值:i.gyazo.com/c24144925d957f36209e1334128fd315.png 我的文件是用 php 中的gzcompress 函数压缩的,知道吗?
  • 嗯,我按照艾萨克的回答成功了! :) 看来我需要先在内存中对其进行 base64 编码?
  • @NiCkNewman 我也有一个数组,你是怎么弄到字符串的?
【解决方案3】:

在我看来,您真正应该问的问题是:如何您如何检索压缩数据?一旦它变成一个 UTF-16 字符串,麻烦就开始了。我什至不确定,从原始字节数据到 javascript 字符串的转换是否无损。

当您写一些关于 php 的内容时,我假设您正在与某种后端进行通信。如果这是真的,则可以选择使用本地方式处理二进制数据。也许这可以帮助你:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data

【讨论】:

  • 另一个问题是我无法控制请求。我正在开发的 chrome 扩展通过chrome.devtools.network 接收数据,它从网络日志中读取数据并从那里获取响应正文。我一直提到的 PHP 只是为了测试解压,但不是应用程序的一部分。
  • 如果您注销前几个解码字节(应该是[120, 156, 237, 157, 91, ...]),您可能会看到一些明显的错误,例如错误的字节序或其他东西?
  • 我的最新研究与此相关。正确的值是 [120,156,237,90,91,83...] 和最新的 JavaScript 解包通过 charCodeAt() 生成:[120, 65533, 65533, 90, 91, 83...] some are 65533 我目前正在研究为什么
  • 由于字符的长度可变,它可能是 UTF-8 编码的。你的 unpack 需要调整,因为每个字节的 MSB 在 UTF-8 中具有特殊含义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 1970-01-01
相关资源
最近更新 更多