fileReader.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)给了我

在这里输入图像说明

上传后显示相同的文件

在这里输入图像说明

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

  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; 

这给了我的输出

在这里输入图像说明

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

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

一些其他可能有用的信息,如果不是使用fileReader.readAsBinaryString()我使用fileObject.getAsBinary()来获取二进制数据,它工作正常。 但getAsBinary只能在Firefox中使用。 我已经在Mac和Firefox上testing了这个,在两者中都得到了相同的结果。 NGINX上传模块正在处理后端上传,再次在Mac上运行。 服务器和客户端在同一台机器上。 我尝试上传的任何文件也是如此,我只是select了PNG,因为它是最明显的例子。

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

(以下是一个很晚但完整的答案)

FileReader方法支持


FileReader.readAsBinaryString()弃用。 不要使用它! 它已不在W3C File API工作草案中 :

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

注意: 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); 

readAsBinaryString()弃用背后的原因在我看来如下:JavaScriptstring的标准是DOMString只接受UTF-8字符,而不是随机的二进制数据。 所以不要使用readAsBinaryString(),这是不安全的,ECMAScript兼容。

我们知道, JavaScriptstring不应该存储二进制数据,但Mozilla在某种程度上可以。 这在我看来是危险的。 Blobtyped arraysArrayBuffer和尚未实现但不必要的StringView )是为了一个目的而发明的:允许使用纯二进制数据,而不受UTF-8string的限制。

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

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

 void sendAsBinary( in DOMString body ); 

sendAsBinary()不是一个标准,在Chrome中可能不支持。

解决scheme


所以你有几个select:

  1. send() FileReader.readAsArrayBuffer ( fileObject )FileReader.readAsArrayBuffer ( fileObject ) 。 这是更复杂的操作(你将不得不作出一个单独的发送()),但这是推荐的方法
  2. send() FileReader.readAsDataURL( fileObject )FileReader.readAsDataURL( fileObject ) 。 它会产生无用的开销和压缩延迟,需要在服务器端进行解压步骤,但是在Javascript中很容易操作为string。
  3. 非标准和sendAsBinary() FileReader.readAsBinaryString( fileObject )FileReader.readAsBinaryString( fileObject )

MDN指出:

发送二进制内容(如file upload)的最佳方式是使用ArrayBuffers或Blob,并结合send()方法。 但是,如果要发送可串化的原始数据,请改用sendAsBinary()方法,或者使用StringView(非本地)types的数组超类。

支持它的浏览器的最好方法是将文件作为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); 

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

 // file is an instance of File, eg 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+)。