【问题标题】:Reading an LZ4 compressed text file (mozlz4) in WebExtensions (JavaScript, Firefox)在 WebExtensions (JavaScript, Firefox) 中读取 LZ4 压缩文本文件 (mozlz4)
【发布时间】:2017-09-09 11:09:51
【问题描述】:

我正在将 Firefox Add-on SDK 扩展移植到 WebExtensions。以前我可以访问浏览器的搜索引擎,但现在我不能,所以一位乐于助人的用户建议我尝试阅读 search.json.mozlz4 文件,该文件包含所有已安装的引擎。但是,这个文件是 LZ4 压缩的 json,它在 Mozilla 的 own LZ4 format 中,带有一个自定义的幻数,'mozLz40\0'。

以前,可以使用它来读取使用 LZ4 压缩的文本文件,包括 mozlz4 文件:

let bytes = OS.File.read(path, { compression: "lz4" });
let content = new TextDecoder().decode(bytes);

(虽然我找不到有关“压缩”字段的文档,但它可以工作)

现在,使用 WebExtensions,我能想到的读取文件的最佳方法是

var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(ev) {
    let content = ev.target.result;
};

这不会以任何方式处理压缩。 This library 处理 LZ4,但它适用于 node.js,所以我不能使用它。 [编辑:它也可以独立工作]。但是,即使我删除了自定义幻数处理,我也无法让它解压缩文件,而相比之下,这个 Python 代码可以按预期工作:

import lz4
file_obj = open("search.json.mozlz4", "rb")
if file_obj.read(8) != b"mozLz40\0":
    raise InvalidHeader("Invalid magic number")
print(lz4.block.decompress(file_obj.read()))

如何在 JS 中做到这一点?

【问题讨论】:

  • 谢天谢地,我弄错了,node-lz4 实际上也可以独立工作,实现require 以避免包装。但是,它似乎无法处理具有自定义 lz4 幻数的特定 mozlz4 格式(即使从数据和相应检查中删除幻数)。我用这一切更新了这个问题。

标签: javascript file firefox-addon-webextensions lz4


【解决方案1】:

经过反复试验,我终于能够在 WebExtension 中读取和解码 search.json.mozlz4 文件。您可以使用node-lz4 library,尽管您只需要一个函数 - uncompress(别名为 decodeBlock 用于外部访问) - 所以我将其重命名为 decodeLz4Block 并将其包含在此处并稍作更改:

// This method's code was taken from node-lz4 by Pierre Curto. MIT license.
// CHANGES: Added ; to all lines. Reformated one-liners. Removed n = eIdx. Fixed eIdx skipping end bytes if sIdx != 0.
function decodeLz4Block(input, output, sIdx, eIdx)
{
    sIdx = sIdx || 0;
    eIdx = eIdx || input.length;

    // Process each sequence in the incoming data
    for (var i = sIdx, j = 0; i < eIdx;)
    {
        var token = input[i++];

        // Literals
        var literals_length = (token >> 4);
        if (literals_length > 0) {
            // length of literals
            var l = literals_length + 240;
            while (l === 255) {
                l = input[i++];
                literals_length += l;
            }

            // Copy the literals
            var end = i + literals_length;
            while (i < end) {
                output[j++] = input[i++];
            }

            // End of buffer?
            if (i === eIdx) {
                return j;
            }
        }

        // Match copy
        // 2 bytes offset (little endian)
        var offset = input[i++] | (input[i++] << 8);

        // 0 is an invalid offset value
        if (offset === 0 || offset > j) {
            return -(i-2);
        }

        // length of match copy
        var match_length = (token & 0xf);
        var l = match_length + 240;
        while (l === 255) {
            l = input[i++];
            match_length += l;
        }

        // Copy the match
        var pos = j - offset; // position of the match copy in the current output
        var end = j + match_length + 4; // minmatch = 4
        while (j < end) {
            output[j++] = output[pos++];
        }
    }

    return j;
}

然后声明这个函数,它接收一个 File 对象(不是路径)和成功/错误的回调:

function readMozlz4File(file, onRead, onError)
{
    let reader = new FileReader();

    reader.onload = function() {
        let input = new Uint8Array(reader.result);
        let output;
        let uncompressedSize = input.length*3;  // size estimate for uncompressed data!

        // Decode whole file.
        do {
            output = new Uint8Array(uncompressedSize);
            uncompressedSize = decodeLz4Block(input, output, 8+4);  // skip 8 byte magic number + 4 byte data size field
            // if there's more data than our output estimate, create a bigger output array and retry (at most one retry)
        } while (uncompressedSize > output.length);

        output = output.slice(0, uncompressedSize); // remove excess bytes

        let decodedText = new TextDecoder().decode(output);
        onRead(decodedText);
    };

    if (onError) {
        reader.onerror = onError;
    }

    reader.readAsArrayBuffer(file); // read as bytes
};

然后,您可以在插件设置页面中添加一个 HTML 按钮,让用户搜索并选择 search.json.mozlz4(在 WebExtensions 中,您不能在没有用户干预的情况下简单地打开文件系统中的任何文件):

<input name="selectMozlz4FileButton" type="file" accept=".json.mozlz4">

要响应用户选择文件,使用类似这样的东西,它调用我们之前声明的方法(这里我不使用错误回调,但你可以):

let button = document.getElementsByName("selectMozlz4FileButton")[0];
button.onchange = function onButtonPress(ev) {
    let file = ev.target.files[0];
    readMozlz4File(file, function(text){
        console.log(text);
    });
};

我希望这对某人有所帮助。我确实花了很多时间来解决这个简单的问题。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2013-11-16
    • 1970-01-01
    相关资源
    最近更新 更多