Я переношу расширение SDK надстройки Firefox в WebExtensions. Раньше я мог получить доступ к поисковым системам браузера, но теперь я не могу, поэтому полезный пользователь предложил попробовать прочитать файл search.json.mozlz4, в котором есть каждый установленный движок. Тем не менее, этот файл json с компрессией LZ4, а в Mozilla собственный формат LZ4 с настраиваемым магическим номером "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;
};
Это никак не обрабатывает сжатие. Эта библиотека обрабатывает LZ4 , но это для node.js, поэтому я не могу это использовать. [edit: он тоже работает автономно]. Тем не менее, даже если я удаляю пользовательскую обработку магического числа, я не могу заставить его распаковать файл, в то время как этот код 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?
После долгих проб и ошибок я наконец смог прочитать и декодировать файл search.json.mozlz4 в WebExtension. Вы можете использовать библиотеку узлов LZ4, хотя вам нужно всего лишь одну функцию - uncompress
(псевдонимами, как decodeBlock
для внешнего доступа) - поэтому я переименовал его в decodeLz4Block
и включил его здесь с небольшими изменениями:
// This method 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 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);
});
};
Я надеюсь, что это помогает кому-то. Я, конечно, потратил много времени на проработку этой простой вещи. :)
require
чтобы избежать упаковщиков. Тем не менее, он, похоже, не обрабатывает определенный формат mozlz4, который имеет собственный магический номер lz4 (даже если удалить магический номер из данных и соответствующую проверку). Я обновил вопрос со всем этим.