fileReader.readAsBinaryString 上传文件
Posted
技术标签:
【中文标题】fileReader.readAsBinaryString 上传文件【英文标题】:fileReader.readAsBinaryString to upload files 【发布时间】:2011-11-17 21:12:49 【问题描述】:尝试使用 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 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 和 Chrome 中对此进行测试,两者都得到了相同的结果。后端上传由nginx Upload Module 处理,再次在 Mac 上运行。服务器和客户端在同一台机器上。我尝试上传的任何文件都会发生同样的情况,我只是选择了 PNG,因为它是最明显的例子。
【问题讨论】:
【参考方案1】:使用fileReader.readAsDataURL( fileObject )
,这会将其编码为base64,您可以安全地将其上传到您的服务器。
【讨论】:
虽然可行,但保存在服务器上的文件版本是 Base64 编码的(应该是)。有没有办法将它作为二进制数据而不是 Base64 编码传输(即好像它是使用普通的<input type="file">
字段上传的一样)
如果服务器上有 php,则可以在存储之前使用 base64_decode(file)。不——没有通过 http 传输原始二进制数据的安全方法。
使用 readAsDataURL 在服务器上给了我这个 imgur.com/1LHya,通过 PHP 的 base64_decode 运行它(我们实际上使用的是 Python,但 PHP 是一个很好的测试)我得到 imgur.com/0uwhy,仍然不是原始二进制数据而不是有效图像 =(
@imgur.com/1LHya 哦,我的错!在服务器上,您必须用 "," 拆分 base64 字符串并仅存储第二部分 - 因此 mime 类型不会与实际文件内容一起存储。
不,它没有效率。这将增加文件大小 137% 并增加服务器开销。但是没有其他方式支持F*** IE【参考方案2】:
(以下是一个较晚但完整的答案)
FileReader 方法支持
FileReader.readAsBinaryString()
已弃用。不要使用它!它不再在W3C File API working draft:
void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);
注意:注意File
是一种扩展的Blob
结构。
Mozilla 仍然实现readAsBinaryString()
并在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);
在我看来,readAsBinaryString()
弃用的原因如下:javascript 字符串的标准是 DOMString
,它只接受 UTF-8 字符,而不是随机二进制数据。所以不要使用 readAsBinaryString(),这根本不安全且不符合 ECMAScript。
我们知道 JavaScript 字符串不应该存储二进制数据,但 Mozilla 在某种程度上可以。在我看来这很危险。 Blob
和 typed 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);
XMLHttpRequest.sendAsBinary()
具有以下调用选项:
void sendAsBinary( in DOMString body );
sendAsBinary() 不是标准,Chrome 可能不支持。
解决方案
所以你有几个选择:
send()
FileReader.result
的 FileReader.readAsArrayBuffer ( fileObject )
。操作起来更复杂(您必须为它创建一个单独的 send()),但这是推荐的方法。
send()
FileReader.result
的 FileReader.readAsDataURL( fileObject )
。它会产生无用的开销和压缩延迟,需要在服务器端进行解压缩步骤,但它很容易在 Javascript 中作为字符串进行操作。
非标准和sendAsBinary()
FileReader.result
的FileReader.readAsBinaryString( fileObject )
MDN 声明:
发送二进制内容(如文件上传)的最佳方式是使用 ArrayBuffers 或 Blob 与 send() 方法结合使用。然而, 如果要发送可字符串化的原始数据,请使用 sendAsBinary() 方法,或 StringView(非本机)类型数组超类。
【讨论】:
我很抱歉再次挖掘这个,只是想补充一点,发送二进制数据(等 PDF 文件)的最简单方法可能是通过FileReader.readAsDataURL
和 onload
处理程序而不是只需发送event.target.result
(这不是一个干净的base64编码字符串),您首先使用一些正则表达式(如event.target.result = event.target.result.match(/,(.*)$/)[1]
)对其进行清理,然后将真正的base64发送到服务器进行解码。
由于任何人都可以编辑 MDN,我可能不会将其用作来源。
@user1299518,最好使用event.target.result.split(",", 2)[1]
,而不是match
。
@KrisWebDev:在推荐的选项中,您提到需要单独发送()。为什么?
推荐的方法适用于使用 TFS REST API 上传附件。谢谢!【参考方案3】:
在支持它的浏览器中,最好的方法是将文件作为 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
。请参阅documentation。当然,这也需要在服务器端进行更改。
// 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+)。
【讨论】:
xmlHttpRequest.send(formData); 最后一个正确的答案也没有使用FormData
。似乎每个人都在使用表单,而他们所需要的只是上传一个文件......谢谢!
我一直在寻找如何让这个通过 ajax 上传 mp3 文件的工作,这可以解决问题!
我认为你可能不需要做的一件事 setRequestHeader 因为它将通过发送 formData 自动设置,并且看起来像这样 "Content-Type: multipart/form-data; boundary=--- -WebKitFormBoundaryQA8d7glpaso6zKsA" 就我而言,它破坏了 CORS,除非我删除了 setRequestHeader。
注意:我上面的评论只适用于使用 formData 对象时。以上是关于fileReader.readAsBinaryString 上传文件的主要内容,如果未能解决你的问题,请参考以下文章