文本到数组缓冲区会导致文件损坏

时间:2019-04-15 14:42:50

标签: javascript html filereader

我有一个示例,用户可以从中选择一个文件(特别是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);的{​​{1}} istead。在那种情况下,我会将文本转换为数组缓冲区,然后尝试做同样的事情。

reader.readAsArrayBuffer(file);

现在,如果我传递的PDF文件较小,并且只有文本,则可以使用该文件,但是当选择较大的文件和/或其中包含图像的文件时,将会下载计算的文件。

现在,我确实知道我正在努力使自己的生活更艰难。但是我想做的是以某种方式将结果从$(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); } 转换为arrayBuffer,以便readAsText()readAsText()都一样工作。

3 个答案:

答案 0 :(得分:3)

readAsText method不仅使UCS-16字符串中的字节可访问。相反,它会根据给定的文本编码格式将它们解码为文本,默认情况下为UTF-8。这将与您尝试读取的任何二进制数据混淆。正如您已经知道的那样,使用readAsArrayBuffer

您可以尝试使用TextEncoderencode将文本返回到类型化数组,但这不能保证产生相同的结果:BOM被剥离,无效的UTF-8序列导致错误,如果不幸的话,甚至会发生Unicode标准化。

如果您明确指定单字节解码,可能会变得更容易,但实际上应该只使用readAsArrayBuffer

答案 1 :(得分:1)

Bergi已经回答了,您应该对二进制数据使用readAsArrayBuffer而不是readAsText,因为后者会解码字节序列,默认情况下为UTF-8。

UTF-8是可变长度编码,其中字符可以在1到4个字节之间。在不是UTF-8的二进制数据上运行解码器将无法恢复破坏二进制数据。

例如,仅0x00-0x7F逐字复制。 0xC2至0xDF是2字节序列的起始序列,是3字节序列的0xE0至0xEF,是4字节序列的0xF0至0xFF。 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是替换字符,当无法将输入转换为已知代码点时使用。

答案 2 :(得分:-1)

这可能是我很久以前使用图形文件时遇到的问题。二进制文件具有特定格式是有原因的,例如cr / lf之类的东西可能就合法了。通过读取二进制文件作为文本并将其写回,实际上可能会在每行中额外增加cr / lf,从而丢弃文件中的原始格式/内容/指针。

要确认这一点,我将把您的原始文件,作为数组缓冲区读取/写入的内容带到一个Test文件,然后对文本作为文本的读取/写入的方式执行相同的操作。然后在两个文件之间进行二进制比较。

我敢打赌,您会无意间在里面得到多余的东西。