在 WebExtensions (JavaScript, Firefox) 中读取 LZ4 压缩文本文件 (mozlz4)
Reading an LZ4 compressed text file (mozlz4) in WebExtensions (JavaScript, Firefox)
我正在将 Firefox Add-on SDK 扩展移植到 WebExtensions。以前我可以访问浏览器的搜索引擎,但现在我不能,所以一位乐于助人的用户建议我尝试阅读 search.json.mozlz4 文件,其中包含所有已安装的引擎。但是,这个文件是 json,采用 LZ4 压缩,它在 Mozilla 的 own LZ4 format 中,带有自定义幻数 'mozLz40[=31=]'.
以前,可以使用它来读取使用 LZ4 压缩的文本文件,包括 mozlz4 文件:
let bytes = OS.File.read(path, { compression: "lz4" });
let content = new TextDecoder().decode(bytes);
(虽然我找不到关于 "compression" 字段的文档,但它有效)
现在,使用 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[=13=]":
raise InvalidHeader("Invalid magic number")
print(lz4.block.decompress(file_obj.read()))
如何在 JS 中执行此操作?
经过反复试验,我终于能够读取和解码 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;
}
然后声明这个接收文件对象(不是路径)和回调的函数 success/error:
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 按钮到你的插件设置页面,让用户搜索和 select search.json.mozlz4(在 WebExtensions 中你不能简单地打开文件系统中的任何文件,无需用户干预):
<input name="selectMozlz4FileButton" type="file" accept=".json.mozlz4">
要响应用户 select 文件,使用类似这样的东西,它调用我们之前声明的方法(这里我不使用错误回调,但你可以):
let button = document.getElementsByName("selectMozlz4FileButton")[0];
button.onchange = function onButtonPress(ev) {
let file = ev.target.files[0];
readMozlz4File(file, function(text){
console.log(text);
});
};
我希望这对某人有所帮助。我确实花了很多时间来解决这个简单的问题。 :)
我正在将 Firefox Add-on SDK 扩展移植到 WebExtensions。以前我可以访问浏览器的搜索引擎,但现在我不能,所以一位乐于助人的用户建议我尝试阅读 search.json.mozlz4 文件,其中包含所有已安装的引擎。但是,这个文件是 json,采用 LZ4 压缩,它在 Mozilla 的 own LZ4 format 中,带有自定义幻数 'mozLz40[=31=]'.
以前,可以使用它来读取使用 LZ4 压缩的文本文件,包括 mozlz4 文件:
let bytes = OS.File.read(path, { compression: "lz4" });
let content = new TextDecoder().decode(bytes);
(虽然我找不到关于 "compression" 字段的文档,但它有效)
现在,使用 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[=13=]":
raise InvalidHeader("Invalid magic number")
print(lz4.block.decompress(file_obj.read()))
如何在 JS 中执行此操作?
经过反复试验,我终于能够读取和解码 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;
}
然后声明这个接收文件对象(不是路径)和回调的函数 success/error:
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 按钮到你的插件设置页面,让用户搜索和 select search.json.mozlz4(在 WebExtensions 中你不能简单地打开文件系统中的任何文件,无需用户干预):
<input name="selectMozlz4FileButton" type="file" accept=".json.mozlz4">
要响应用户 select 文件,使用类似这样的东西,它调用我们之前声明的方法(这里我不使用错误回调,但你可以):
let button = document.getElementsByName("selectMozlz4FileButton")[0];
button.onchange = function onButtonPress(ev) {
let file = ev.target.files[0];
readMozlz4File(file, function(text){
console.log(text);
});
};
我希望这对某人有所帮助。我确实花了很多时间来解决这个简单的问题。 :)