Разделение и перестройка файла в nodejs изменяет его длину в некоторых форматах - PullRequest
1 голос
/ 03 июня 2019

Я пишу скрипт для разделения файлов на куски и перестройки этих файлов из кусков.

Это прекрасно работает для .txt файлов и .js файлов

Однако, когда я пробую свой сценарий с более «сложными» файлами, такими как изображения .png, .pdf или .docx, файл будет перестроен неправильно.

Например, для изображения длиной 2 057 bytes я извлекаю файл длиной 3 387 bytes, который не может быть прочитан ничем.

Я не знаю, как это отладить. Поэтому я был бы признателен, если бы кто-то указал на то, что не так или как найти шляпу не так с моей работой.

Вот мой сценарий:

const fs = require('fs');
const fileName = "sample.png";
var fileBuffer = fs.readFileSync(fileName);
var bufferLength = fileBuffer.length;
var chunkSize = 10;
var chunkQtt = bufferLength / chunkSize;
var result = splitBuffer(fileBuffer, chunkSize);

result.forEach(writeFileFromBuf);
var resFromChunk = [];

for (let i = 0; i < result.length; i++) {
    resFromChunk.push(fs.readFileSync('./chunks/fileChunk' + i));
}


fs.createWriteStream("rebuilt_" + fileName).write(Buffer.concat(resFromChunk).toString());

console.log("quantity of chunks: " + chunkQtt);
console.log("buffer length " + bufferLength);
console.log("chunks size " + chunkSize);

function splitBuffer(buffer, csize) {
    let pointer = 0;
    var result = [];
    var currentSize = 0;

    for (let i = 0; i < chunkQtt; i++) {
        result[i] = buffer.slice(pointer, pointer + csize);
        console.log("length " + result[i].length + " data: " + result[i].toString());
        currentSize += result[i].length;
        pointer += csize;
    }
    console.log("retrieved size " + currentSize)
    return result
}

function writeFileFromBuf(element, index, array) {
    var fileName = "./chunks/fileChunk" + index;
    fs.writeFileSync(fileName, element);
}

1 Ответ

1 голос
/ 07 июня 2019

Все в порядке, пока вы не дойдете до точки, где вы попытаетесь склеить все прочитанные фрагменты вместе и записать результат в повторно собранный файл "rebuilt_sample.png". Все шаги до этого момента (чтение исходного файла, разбиение его на куски, запись фрагментов в отдельные файлы, чтение файлов фрагментов обратно) обрабатывают данные файла как буферы, содержащие байты, которые не были проверены или интерпретированы в любым способом.

Но тогда это происходит:

fs.createWriteStream("rebuilt_" + fileName).write(Buffer.concat(resFromChunk).toString());

В этом операторе вызов toString() приводит к тому, что байты в объединенном буфере интерпретируются (или «декодируются») как символы, а затем во время операции write() эти символы преобразуются (или «кодируются») обратно. в байтовый поток, который записывается в файл. Действия по декодированию и кодированию выполняются в соответствии с правилами локали вашей программы, которая, вероятно, является локалью UTF-8. UTF-8 - это метод для перевода символов Юникода в последовательность байтов и из нее.

Отлично IF Все байты в буфере могут быть успешно декодированы как символы по правилам UTF-8. Это очень вероятно, когда входной файл представляет собой текстовый файл, например .txt или .js. Но когда файл представляет собой нетекстовый файл (часто называемый «двоичным» файлом), вполне вероятно, что некоторые последовательности байтов в файле не будут допустимыми последовательностями байтов UTF-8, и поэтому эти байты не будут переводиться в символы , Это произойдет почти для всех байтов, значение которых больше 127 десятичных, 0x7F шестнадцатеричных. Ваш sample.png является одним из этих двоичных файлов, который содержит некоторые байты, которые не могут быть переведены в символы.

Есть несколько способов, которыми программа может реагировать на непереводимые байты. Например, может произойти сбой, он может молча пропустить эти байты, он может пропустить их и выдать отчет об ошибке, он может прекратить перевод после просмотра неверного байта, он может создать полностью пустую строку. В этом случае узел тихо преобразует каждый недопустимый байт в символ замены Unicode « » в сгенерированной строке. Затем, когда эта строка кодируется обратно в байты во время операции write(), каждый из этих символов замещения преобразуется в соответствии с правилами UTF-8 и становится трехбайтовой последовательностью 0xEF 0xBF 0xFD в восстановленном файле.

Вот почему ваш восстановленный файл больше оригинального. Почти каждый байт в исходном файле, значение которого было больше 0x7F, заменяется на три байта в перестроенном файле.

Исправить эту проблему легко. Просто избавьтесь от вызова .toString() в этой строке и позвольте вашей программе обрабатывать объединенный буфер как не интерпретируемый набор байтов:

fs.createWriteStream("rebuilt_" + fileName).write(Buffer.concat(resFromChunk))
...