fileReader.readAsBinaryString 上传文件

IT技术 javascript encoding upload filereader
2021-03-14 04:24:31

尝试使用 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 上传module处理,再次在 Mac 上运行。服务器和客户端在同一台机器上。我尝试上传的任何文件都会发生同样的事情,我只是选择了 PNG,因为它是最明显的例子。

3个回答

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

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()我认为弃用的原因如下:JavaScript 字符串的标准是DOMString只接受 UTF-8 字符,而不是随机二进制数据。所以不要使用 readAsBinaryString(),这根本不安全且不符合 ECMAScript 标准。

我们知道JavaScript 字符串不应该存储二进制数据,但 Mozilla 可以。在我看来这很危险。Blobtyped arraysArrayBuffer以及尚未实现但不是必需的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 可能不支持。

解决方案


所以你有几个选择:

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

MDN指出:

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

由于任何人都可以编辑 MDN,因此我可能不会将其用作来源。
2021-04-16 04:24:31
@user1299518,更好地使用event.target.result.split(",", 2)[1],而不是match
2021-04-17 04:24:31
推荐的方法适用于使用 TFS REST API 上传附件。谢谢!
2021-04-20 04:24:31
我很抱歉再次挖这一点,只是想补充一点,可能是发送二进制数据(等PDF文件)最简单的方法就是通过FileReader.readAsDataURLonload处理程序,而不是仅仅发送的event.target.result(这是不干净的base64编码字符串)你首先用一些正则表达式清理它,event.target.result = event.target.result.match(/,(.*)$/)[1]然后将真正的 base64 发送到服务器进行解码。
2021-04-21 04:24:31
@KrisWebDev:在推荐选项中,您提到需要单独发送()。为什么?
2021-04-21 04:24:31

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

不,它效率不高。这将增加文件大小 137% 并增加服务器开销。但是没有其他方法可以支持 F*** IE
2021-04-19 04:24:31
如果服务器上有 PHP,则可以在存储之前使用 base64_decode(file)。不 - 没有通过 http 传输原始二进制数据的安全方法。
2021-04-27 04:24:31
@ imgur.com / 1LHya哦,我的错!在服务器上,您必须将 base64 字符串拆分为“,”并仅存储第二部分 - 因此 MIME 类型不会与实际文件内容一起存储。
2021-05-01 04:24:31
虽然这样做有效,但保存在服务器上的文件版本是 Base64 编码的(应该是这样)。有没有办法将它作为二进制数据而不是 Base64 编码传输(IE 好像它是使用普通<input type="file">字段上传的
2021-05-04 04:24:31
使用 readAsDataURL在服务器上给了我这个imgur.com/1LHya,通过 PHP 的 base64_decode 运行它(我们实际上使用的是 Python,但 PHP 是一个很好的测试)我得到了imgur.com/0uwhy,仍然不是原始的二进制数据和不是有效的图像 =(
2021-05-05 04:24:31

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

注意:我上面的评论仅适用于使用 formData 对象时。
2021-04-26 04:24:31
我一直在寻找如何通过 ajax 上传 mp3 文件的方法,这就是窍门!
2021-05-01 04:24:31
xmlHttpRequest.send(formData);
2021-05-03 04:24:31
最后一个正确的答案也没有使用FormData. 似乎每个人都在使用表单,而他们只需要上传一个文件……谢谢!
2021-05-07 04:24:31
我认为你可能不需要做 setRequestHeader 的一件事,因为它将通过发送 formData 自动设置,并且看起来像这样“Content-Type:multipart/form-data;boundary=----WebKitFormBoundaryQA8d7glpaso6zKsA”在我的情况下它坏了CORS 除非我删除了 setRequestHeader。
2021-05-13 04:24:31