关于javascript:fileReader.readAsBinaryString上传文件

fileReader.readAsBinaryString to upload files

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 ="\
\
"
;

    //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);

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

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    function utf8encode(string) {
        string = string.replace(/\
\
/g,"\
"
);
        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;
    )

然后使用原始代码

1
2
//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;

这给了我

的输出

enter

1
2
3
4
void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);

NB:请注意,File是一种扩展的Blob结构。

Mozilla仍实现readAsBinaryString(),并在MDN FileApi文档中进行了描述:

1
2
3
4
5
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);

在我看来,readAsBinaryString()弃用的原因如下:JavaScript字符串的标准是DOMString,它仅接受UTF-8字符,而不接受随机二进制数据。因此,请勿使用readAsBinaryString(),因为它不安全且完全符合ECMAScript。

我们知道JavaScript字符串不应该存储二进制数据,但是Mozilla可以存储二进制数据。我认为这很危险。出于以下目的发明了Blobtyped arrays(ArrayBuffer和尚未实现但不是必需的StringView):允许使用纯二进制数据,而没有UTF-8字符串限制。

XMLHttpRequest上传支持

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

1
2
3
4
5
6
void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

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

1
void sendAsBinary(   in DOMString body );

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

解决方案

因此您有几种选择:

  • send() FileReader.readAsArrayBuffer ( fileObject )FileReader.result。操作起来更加复杂(您必须为此单独创建一个send()),但这是推荐的方法。
  • send() fileReader.readAsDataURL( fileObject )FileReader.result。它产生无用的开销和压缩延迟,需要在服务器端BUT上执行解压缩步骤,因此很容易将其作为Javascript中的字符串进行操作。
  • 是非标准的,并且是FileReader.readAsBinaryString( fileObject )FileReader.result
  • MDN指出:

    The best way to send binary content (like in files upload) is using
    ArrayBuffers or Blobs in conjuncton with the send() method. However,
    if you want to send a stringifiable raw data, use the sendAsBinary()
    method instead, or the StringView (Non native) typed arrays superclass.


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


    在支持该功能的浏览器中,最好的方法是将文件作为Blob发送,或者如果需要多部分表单,则使用FormData。您不需要为此的FileReader。与尝试读取数据相比,这既简单又有效。

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

    1
    2
    3
    4
    5
    6
    7
    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);

    您也可以直接发送数据,而不使用multipart/form-data。请参阅文档。当然,这也需要在服务器端进行更改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 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);

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