ReadAsBinaryString 上传文件

尝试使用 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);

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

enter image description here

上传后显示相同的文件

enter image description here

这看起来像是一个格式/编码问题,我尝试在原始二进制数据上使用一个简单的 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;
)

然后在原始代码中

//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 image description here

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

如何对文件进行编码/加载/处理以避免编码问题,从而使 HTTP 请求中接收的文件与上传前的文件相同。

如果我不使用 fileReader.readAsBinaryString () ,而是使用 fileObject.getAsBinary ()来获取二进制数据,那么它可以很好地工作。但是 getAsBinary 只能在 Firefox 中工作。我已经在 Firefox 和 Chrome 上测试过了,都是在 Mac 上,结果都是一样的。后端上传由 上传模块处理,同样运行在 Mac 上。服务器和客户端在同一台机器上。同样的事情也发生在我尝试上传的任何文件上,我只是选择了 PNG,因为它是最明显的例子。

148597 次浏览

Use fileReader.readAsDataURL( fileObject ), this will encode it to base64, which you can safely upload to your server.

(Following is a late but complete answer)

FileReader methods support


FileReader.readAsBinaryString() is deprecated. Don't use it! It's no longer in the W3C File API working draft:

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);

NB: Note that File is a kind of extended Blob structure.

Mozilla still implements readAsBinaryString() and describes it in MDN FileApi documentation:

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

The reason behind readAsBinaryString() deprecation is in my opinion the following: the standard for JavaScript strings are DOMString which only accept UTF-8 characters, NOT random binary data. So don't use readAsBinaryString(), that's not safe and ECMAScript-compliant at all.

We know that JavaScript strings are not supposed to store binary data but Mozilla in some sort can. That's dangerous in my opinion. Blob and typed arrays (ArrayBuffer and the not-yet-implemented but not necessary StringView) were invented for one purpose: allow the use of pure binary data, without UTF-8 strings restrictions.

XMLHttpRequest upload support


XMLHttpRequest.send() has the following invocations options:

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

XMLHttpRequest.sendAsBinary() has the following invocations options:

void sendAsBinary(   in DOMString body );

sendAsBinary() is NOT a standard and may not be supported in Chrome.

Solutions


So you have several options:

  1. send() the FileReader.result of FileReader.readAsArrayBuffer ( fileObject ). It is more complicated to manipulate (you'll have to make a separate send() for it) but it's the RECOMMENDED APPROACH.
  2. send() the FileReader.result of FileReader.readAsDataURL( fileObject ). It generates useless overhead and compression latency, requires a decompression step on the server-side BUT it's easy to manipulate as a string in Javascript.
  3. Being non-standard and sendAsBinary() the FileReader.result of FileReader.readAsBinaryString( fileObject )

MDN states that:

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.

The best way in browsers that support it, is to send the file as a Blob, or using FormData if you want a multipart form. You do not need a FileReader for that. This is both simpler and more efficient than trying to read the data.

If you specifically want to send it as multipart/form-data, you can use a FormData object:

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

You can also send the data directly, instead of using multipart/form-data. See the documentation. Of course, this will need a server-side change as well.

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

For browser support, see: http://caniuse.com/#feat=xhr2 (most browsers, including IE 10+).