fileReader.readAsBinaryString上传文件

Smu*_*dge 78 javascript upload encoding filereader

尝试使用fileReader.readAsBinaryString通过AJAX将PNG文件上传到服务器,剥离代码(fileObject是包含我文件信息的对象);

var fileReader = new FileReader();

fileReader.onload = function(e) {
    var xmlHttpRequest = new XMLHttpRequest();
    //Some AJAX-y stuff - callbacks, handlers etc.
    xmlHttpRequest.open("POST", '/pushfile', true);
    var dashes = '--';
    var boundary = 'aperturephotoupload';
    var crlf = "\r\n";

    //Post with the correct MIME type (If the OS can identify one)
    if ( fileObject.type == '' ){
        filetype = 'application/octet-stream';
    } else {
        filetype = fileObject.type;
    }

    //Build a HTTP request to post the file
    var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(fileObject.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + e.target.result + crlf + dashes + boundary + dashes;

    xmlHttpRequest.setRequestHeader("Content-Type", "multipart/form-data;boundary=" + boundary);

    //Send the binary data
    xmlHttpRequest.send(data);
}

fileReader.readAsBinaryString(fileObject);
Run Code Online (Sandbox Code Playgroud)

在上传之前检查文件的前几行(使用VI)给了我

在此输入图像描述

上传后显示相同的文件

在此输入图像描述

所以它看起来像某个地方的格式化/编码问题,我尝试在原始二进制数据上使用简单的UTF8编码函数

    function utf8encode(string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    )
Run Code Online (Sandbox Code Playgroud)

然后在原始代码中

//Build a HTTP request to post the file
var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(file.file.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + utf8encode(e.target.result) + crlf + dashes + boundary + dashes;
Run Code Online (Sandbox Code Playgroud)

这给了我的输出

在此输入图像描述

仍然不是原始文件是什么=(

如何编码/加载/处理文件以避免编码问题,因此HTTP请求中接收的文件与上载之前的文件相同.

一些其他可能有用的信息,如果不使用fileReader.readAsBinaryString()我使用fileObject.getAsBinary()来获取二进制数据,它工作正常.但getAsBinary仅适用于Firefox.我一直在Firefox和Chrome上对它进行测试,两者都在Mac上获得相同的结果.后端上传由NGINX上传模块处理,再次在Mac上运行.服务器和客户端位于同一台计算机上.我尝试上传的任何文件都会发生同样的事情,我只选择了PNG,因为这是最明显的例子.

Kri*_*Dev 101

(以下是一个迟到但完整的答案)

FileReader方法支持


FileReader.readAsBinaryString()弃用.不要用它!它不再出现在W3C File API工作草案中:

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);
Run Code Online (Sandbox Code Playgroud)

注意:请注意,这File是一种扩展Blob结构.

Mozilla仍然readAsBinaryString()MDN FileApi文档中实现并描述它:

void abort();
void readAsArrayBuffer(in Blob blob); Requires Gecko 7.0
void readAsBinaryString(in Blob blob);
void readAsDataURL(in Blob file);
void readAsText(in Blob blob, [optional] in DOMString encoding);
Run Code Online (Sandbox Code Playgroud)

readAsBinaryString()在我看来,弃用的原因如下:JavaScript字符串的标准DOMString只接受UTF-8字符,而不是随机二进制数据.所以不要使用readAsBinaryString(),这根本不安全且符合ECMAScript.

我们知道JavaScript字符串不应该存储二进制数据,但某种类型的Mozilla可以存储.在我看来这很危险.Blobtyped arrays(ArrayBuffer并且尚未实现但不必要StringView)是出于一个目的而发明的:允许使用纯二进制数据,不受UTF-8字符串限制.

XMLHttpRequest上传支持


XMLHttpRequest.send() 具有以下调用选项:

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);
Run Code Online (Sandbox Code Playgroud)

XMLHttpRequest.sendAsBinary() 具有以下调用选项:

void sendAsBinary(   in DOMString body );
Run Code Online (Sandbox Code Playgroud)

sendAsBinary()不是标准,Chrome可能不支持.

解决方案


所以你有几个选择:

  1. send()FileReader.resultFileReader.readAsArrayBuffer ( fileObject ).操作起来比较复杂(你必须为它做一个单独的send()),但这是推荐的方法.
  2. send()FileReader.resultFileReader.readAsDataURL( fileObject ).它会产生无用的开销和压缩延迟,需要在服务器端进行解压缩步骤但是它很容易在Javascript中作为字符串进行操作.
  3. 作为非标及sendAsBinary()FileReader.resultFileReader.readAsBinaryString( fileObject )

MDN声明:

发送二进制内容的最佳方式(如在文件上传中)使用ArrayBuffers或Blobs与send()方法结合使用.但是,如果要发送可串行的原始数据,请使用sendAsBinary()方法,或使用StringView(非本机)类型的数组超类.

  • 我很遗憾再次挖掘它,只是想补充一点,发送二进制数据(等PDF文件)的最简单方法可能是通过`FileReader.readAsDataURL`和`onload`处理程序而不是发送`事件.你使用像event.target.result = event.target.result.match(/,(.*)$ /)[1]这样的正则表达式来清理target.result`(这不是一个干净的base64编码的字符串) `并将真正的base64发送到要解码的服务器. (8认同)
  • @ user1299518,最好使用`event.target.result.split(",",2)[1]`,而不是`match`. (3认同)

c69*_*c69 71

使用fileReader.readAsDataURL( fileObject ),这会将其编码为base64,您可以安全地将其上传到您的服务器.

  • @http://imgur.com/1LHya哦,我的坏!在服务器上,您必须将"base64"字符串拆分为","并仅存储第二部分 - 因此mime类型不会与实际文件内容一起存储. (20认同)
  • 虽然这有效,但保存在服务器上的文件版本是Base64编码的(应该是).有没有办法将它作为二进制数据而不是Base64编码传输(IE就像使用普通的`<input type ="file">`字段上传一样) (7认同)
  • 不,它效率不高.这将增加137%的文件大小并使服务器开销.但没有其他方法可以支持F***IE (7认同)
  • 如果服务器上有PHP,则可以在存储之前使用base64_decode(文件).不 - 没有安全的方法可以通过http传输原始二进制数据. (2认同)

Ral*_*alf 21

支持它的浏览器的最佳方式是将文件作为Blob发送,或者如果需要多部分表单则使用FormData.你不需要FileReader.这比尝试读取数据更简单,更有效.

如果您特别想将其发送为multipart/form-data,则可以使用FormData对象:

var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);
var formData = new FormData();
// This should automatically set the file name and type.
formData.append("file", file);
// Sending FormData automatically sets the Content-Type header to multipart/form-data
xmlHttpRequest.send(formData);
Run Code Online (Sandbox Code Playgroud)

您也可以直接发送数据,而不是使用multipart/form-data.请参阅文档.当然,这也需要服务器端的改变.

// file is an instance of File, e.g. from a file input.
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);

xmlHttpRequest.setRequestHeader("Content-Type", file.type);

// Send the binary data.
// Since a File is a Blob, we can send it directly.
xmlHttpRequest.send(file);
Run Code Online (Sandbox Code Playgroud)

有关浏览器支持,请参阅:http://caniuse.com/#feat=xhr2(大多数浏览器,包括IE 10+).

  • 最后一个正确的答案也没有使用`FormData`.似乎每个人都在使用表单而他们需要的只是上传一个文件...谢谢! (5认同)