【问题标题】:Compressing hexadecimal string using GZIP on the web (Javascript)在 Web 上使用 GZIP 压缩十六进制字符串(Javascript)
【发布时间】:2020-06-01 19:32:34
【问题描述】:

我有一个textarea,其中输入的值被转换成十六进制并拼接起来,如下图:

4f43 5441 1d00 0000 2400 0000 0004 0000
0200 0000 0000 0000 0000 0000 0000 0000
0200 0000 0206 0073 6b79 626f 780e 0073
6b79 626f 7865 732f 7768 6974 6502 0800
6d61 7074 6974 6c65 1900 4f47 5a20 4564
6974 6f72 2066 6972 7374 2065 7665 7220
6d61 7003 6670 7300 0000 0000 0500 0200
0400 0300 0500 0700 0000 0044 0000 0044
0000 0044 0000 0000 0000 00ff 0000 0500
0000 0544 0000 0544 0000 0044 6e00 3200
0000 0000 0000 0200 0207 0007 0007 0001
0001 0001 0000 0202 0003 0004 0005 0006
0007 0000 0202 0003 0004 0005 0006 0007
0000 0202 0003 0004 0005 0006 0007 0000
0200 0000 0000 0007 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0100 0000
0000 0000 0000 0000 0000 0100 0000 0000
0000 0000 0000 0000

我的目标是使用 gzip“在线”(即不使用 zlib 命令行/nodejs)压缩这个十六进制,并使用 blob 使输出可供下载。

这是我目前使用“PAKO”的尝试:

html

<script src="https://cdn.jsdelivr.net/pako/1.0.3/pako.min.js"></script>
<textarea id="input"></textarea>
<button onclick="toHex()">Convert to hex</button>
<a id="downloadbtn">Download as gzip</button>

javascript

var pako = window.pako;
function toHex(){
    input = document.getElementById("input").value;
    hexresult = input.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(2,"0"),"");

    gzipresult = pako.gzip(hexresult, { level: 6 });
    download(gzipresult);
}

function download(data){
    downloadbtn = document.getElementById("downloadbtn");
    var blob = new Blob([ data ], {type : "application/gzip",});

    if (window.navigator.msSaveBlob) {
        navigator.msSaveBlob(blob, "output.gz");
    } else {
        var csvUrl = URL.createObjectURL(blob);
        $('#downloadbtn').attr({
            'download': "output.gz",
            'href': csvUrl
        });
    };
}

但是 PAKO (gzip) 生成的输出与 zlib 生成的输出不匹配,它们不一样...

有没有办法使两者的输出相同?要么 如何使用 JavaScript 为 gzip 正确压缩十六进制字符串?

更新: @Blex 提到了 Buffer.from 的使用,我认为它是 nodejs 原生的东西(我在浏览器中需要它)所以我从 this repo 尝试了 this standalone script,但是生成的文件仍然相同(仍然不同来自 zlib),但无论如何,我感谢您提供帮助的尝试!

比较

输入:“68656c6c6f20776f726c6421”(Hello world!)

zlib 输出(mingw 命令行): 1f8b 0800 4767 4c5e 0003 cb48 cdc9 c957 28cf 2fca 4951 0400 6dc2 b403 0c00 0000

pako 输出: 1f8b 0800 0000 0000 0003 33b3 3033 354b 06c2 3423 0373 73b3 3473 2320 dbc4 c810 00b2 2eed 2a18 0000 00

【问题讨论】:

  • 我已经阅读了一些内容,因为我想知道为什么我的输出会相差一个字节。我发现这个字节是用于声明操作系统类型的标头。在我的例子中,zlib 返回代表 TOPS-20 操作系统的 a,而 Pako 返回代表 Unix3。详情here。如果那个字节是唯一的区别,这对我来说似乎总是如此,那么改变它会很容易。你是这种情况吗?如果没有,您能否提供由两个库处理的相同输入字符串的示例输出?
  • @blex 好的,感谢您的回复,我更新了帖子。不幸的是,几乎所有的输出都被改变了......我认为重要的是要注意我正在使用 Sublime Text 的“十六进制”编码器打开文件。
  • 哦,我正在使用“mingw”在终端上运行gzip &lt; input.txt &gt; output.gz 命令。

标签: javascript html hex gzip zlib


【解决方案1】:

编辑:写这个答案有点太快后,我注意到输出并不完全相同。有一个字节的区别,对应OS type header。 zlib 返回a,代表TOPS-20,Pako 返回3,代表Unix

zlib 返回一个 Buffer,而 pako 返回一个 Uint8Array。要获得缓冲区,您可以这样做:

gzipresult = pako.gzip(hexresult, { level: 6 });
gzipbuffer = Buffer.from(gzipresult);

这将为您提供 确切的 与 zlib 类似的输出。这是一个简单的 NodeJS 脚本来测试和比较两者:

// Don't forget to `npm i -S pako`
const pako = require("pako");
const zlib = require("zlib");

const input = "Hello world!";
const hexStr = input.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(2,"0"),"");

zlib.gzip(hexStr, function(_, zlibResult) {
  const zlibHex = zlibResult.toString("hex");

  const pakoResult = pako.gzip(hexStr, { level: 6 });
  const pakoHex = Buffer.from(pakoResult).toString("hex");

  console.log("zlib", zlibHex);
  console.log("pako", pakoHex);
  console.log("Outputs are equal ===", zlibHex === pakoHex);
});


/*
Output:
pako 1f8b080000000000000333b13033354b06c23423037373b334732320dbc4c81000bde2d6f318000000
zlib 1f8b080000000000000a33b13033354b06c23423037373b334732320dbc4c81000bde2d6f318000000
                        ^
             actually not the same
Outputs are equal === false
*/

【讨论】:

  • 使用nodejs和测试你的脚本也都返回这些值,现在我开始认为我在mingw上使用的gzip版本已经过时了之类的...@ 987654322@但是只有mingw生成的输出对我来说是“有效的”,我以为压缩方法是一样的……
【解决方案2】:

所以,事实证明,这整个混乱的发生是因为我的输入除了是纯文本之外,还有几个空格字符(我为了更好地查看而放置了这些字符),这显然影响了转换为十六进制时的结果。 . 为了让它按我预期的方式工作,我只需要删除所有空格字符,将其编码为十六进制,然后用 pako 压缩。

var pako = window.pako;
function toHex(){
    input = document.getElementById("input").value.replace(/\s/g, "");
    hexresult = new Uint8Array(input.match(/.{2}/g).map(e => parseInt(e, 16)));

    gzipresult = pako.gzip(hexresult);
    download(gzipresult);
}

function download(data){
    downloadbtn = document.getElementById("downloadbtn");
    var blob = new Blob([ data ], {type : "application/octet-stream",});

    if (window.navigator.msSaveBlob) {
        navigator.msSaveBlob(blob, "output.gz");
    } else {
        var csvUrl = URL.createObjectURL(blob);
        $('#downloadbtn').attr({
            'download': "output.gz",
            'href': csvUrl
        })
    }
}

【讨论】:

    猜你喜欢
    • 2012-07-31
    • 2010-12-31
    • 1970-01-01
    • 2016-11-01
    • 2014-03-07
    • 2017-03-21
    • 2012-12-06
    • 2020-11-11
    • 1970-01-01
    相关资源
    最近更新 更多