Текст в буфер массива приводит к повреждению файлов - PullRequest
8 голосов
/ 15 апреля 2019

У меня есть пример, из которого пользователь может выбрать файл (в частности, файлы PDF), преобразовать этот файл в буфер массива, создать его обратно из буфера массива и загрузить этот файл. работает как положено.

<input type="file" id="file_input" class="foo" />
<div id="output_field" class="foo"></div>


$(document).ready(function(){
    $('#file_input').on('change', function(e){
        readFile(this.files[0], function(e) {
            //manipulate with result...
            $('#output_field').text(e.target.result);
            try {           
            var file = new Blob([e.target.result], { type: 'application/pdf' });
            var fileURL = window.URL.createObjectURL(file);
            var seconds = new Date().getTime() / 1000;
            var fileName = "cert" + parseInt(seconds) + ".pdf";
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            a.href = fileURL;
            a.download = fileName;
            a.click();
             }
            catch (err){
            $('#output_field').text(err);
            }
        });     
    });
});

function readFile(file, callback){
    var reader = new FileReader();
    reader.onload = callback
    reader.readAsArrayBuffer(file);
}

Теперь, скажем, я использовал reader.readAsText(file); вместо reader.readAsArrayBuffer(file);. В этом случае я бы преобразовал текст в буфер массива и попытался бы сделать то же самое.

$(document).ready(function(){
    $('#file_input').on('change', function(e){
        readFile(this.files[0], function(e) {
            //manipulate with result...
            try {
            var buf = new ArrayBuffer(e.target.result.length * 2); 
            var bufView = new Uint16Array(buf);
            for (var i=0, strLen = e.target.result.length; i<strLen; i++) {
                     bufView[i] = e.target.result.charCodeAt(i);
            }

            var file = new Blob([bufView], { type: 'application/pdf' });
            var fileURL = window.URL.createObjectURL(file);
            var seconds = new Date().getTime() / 1000;
            var fileName = "cert" + parseInt(seconds) + ".pdf";
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            a.href = fileURL;
            a.download = fileName;
            a.click();
             }
            catch (err){
            $('#output_field').text(err);
            }
        });

    });
});

function readFile(file, callback){
    var reader = new FileReader();
    reader.onload = callback
    reader.readAsText(file);
}

Теперь, если я передал PDF-файл, который имеет небольшой размер и содержит только текст, этот файл будет работать, но при выборе файлов большого размера и / или с изображениями в них будет загружен сокращенный файл.

Теперь я знаю, что пытаюсь усложнить себе жизнь. Но я пытаюсь как-то преобразовать результат из readAsText() в arrayBuffer, чтобы оба из readAsText() и readAsArrayBuffer() работали одинаково.

Ответы [ 3 ]

3 голосов
/ 22 апреля 2019

Метод readAsText не просто делает байты доступными в строке UCS-16. Вместо этого он декодирует их как текст в соответствии с заданным форматом кодировки текста по умолчанию UTF-8. Это испортит любые двоичные данные, которые вы пытаетесь прочитать. Как вы уже поняли, используйте для этого readAsArrayBuffer.

Вы можете попытаться использовать от TextEncoder до encode вашего текста обратно в типизированный массив, но это не гарантирует, что вы получите тот же результат: спецификация получена, недопустимые последовательности UTF-8 привести к ошибкам, и если вам не повезет, произойдет даже нормализация Unicode.

Может быть проще, если вы явно укажете однобайтовое декодирование, но на самом деле вы должны просто использовать readAsArrayBuffer.

1 голос
/ 29 апреля 2019

Поскольку Берги уже ответил, вы должны использовать readAsArrayBuffer для двоичных данных вместо readAsText, так как последний декодирует последовательности байтов по умолчанию как UTF-8.

UTF-8 - кодировка переменной длины, где символ может быть от 1 до 4 байтов. Запуск декодера на двоичных данных, отличных от UTF-8, приведет к необратимому повреждению двоичных данных.

Например, только 0x00-0x7F копируется дословно. От 0xC2 до 0xDF - начальная последовательность 2-байтовой последовательности, от 0xE0 до 0xEF 3-байтовой последовательности и от 0xF0 до 0xFF 4-байтовой последовательности. От 0x80 до 0xBF является частью последовательности.

Вот несколько примеров того, как это повреждается (узел 12.1):

      ORIGINAL        =>  DECODED from UTF-8 to UCS-2  =>                 ENOCDED from UCS-2 to UTF-8
----------------------------------------------------------------------------------------------------------------------
[0xC2,0x80,0x80,0x80] => [0x0080,0xFFFD,0xFFFD]        => [0xC2,0x80,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]
[0xC3,0x80,0x80,0x80] => [0x00C0,0xFFFD,0xFFFD]        => [0xC3,0x80,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]
[0xE0,0x80,0x80,0x80] => [0xFFFD,0xFFFD,0xFFFD,0xFFFD] => [0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]
[0xE1,0x80,0x80,0x80] => [0x1000,0xFFFD]               => [0xE1,0x80,0x80,0xEF,0xBF,0xBD]
[0xF0,0x80,0x80,0x80] => [0xFFFD,0xFFFD,0xFFFD,0xFFFD] => [0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]
[0xF1,0x80,0x80,0x80] => [0xD8C0,0xDC00]               => [0xF1,0x80,0x80,0x80]
[0xF0,0x80,0x00,0x00] => [0xFFFD,0xFFFD,0x0000,0x0000] => [0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0x00,0x00]
[0x80,0x80,0x80,0x80] => [0xFFFD,0xFFFD,0xFFFD,0xFFFD] => [0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]
[0x81,0x82,0x83,0x84] => [0xFFFD,0xFFFD,0xFFFD,0xFFFD] => [0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD,0xEF,0xBF,0xBD]

0xFFFD - это символ замены , который используется, когда вход не может быть преобразован в известную кодовую точку.

0 голосов
/ 22 апреля 2019

Это может быть то, с чем я столкнулся при работе с графическими файлами. Двоичные файлы имеют определенный формат по определенной причине, и такие вещи, как cr / lf, могут быть законными в своем собственном месте. Считывая двоичный файл как текст и записывая его обратно, можно фактически добавить дополнительные cr / lf на строку, отбрасывая, таким образом, исходный формат / content / pointers в файле.

Чтобы подтвердить это, я бы взял исходный файл, считал / записал как буфер массива в один тестовый файл, затем сделал бы то же самое с чтением / записью как текст в файл SecondTest. Затем сделайте двоичное сравнение между двумя файлами.

Могу поспорить, что вы случайно получаете дополнительные вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...