如何以Raw POST方式发送二进制数据?POST数据量异常排查
问题:POST二进制数据时请求数据量与实际文件大小不符
我使用以下代码向REST接口以POST方式发送二进制数据:
var file = $("#file-to-upload")[0].files[0]; var filename = file.name; var url = "/upload"; const reader = new FileReader(); reader.onload = function(e) { console.log(`Read file data: length: ${e.target.result.length}`); $.post({ url: url, data: e.target.result, headers: { "Content-Type": "application/octet-stream", "Content-Disposition": `attachment; filename=${filename}` }, success: function(r) { console.log(r); } }).fail(function(xhr, status, error) { console.log(xhr); }); } reader.readAsBinaryString(file);
控制台正确显示了文件的字节数,但在开发者工具中查看实际POST请求时,发现发送的数据量更多。e.target.result是未序列化的二进制字符串,但开发者控制台显示为26.8kB,而字符串长度为12817。请问我忽略了什么?
原因与解决方案
问题根源
readAsBinaryString的局限性:这个方法返回的二进制字符串,本质是把每个字节转成对应ASCII字符,但遇到ASCII范围外的字节(如0x80及以上),会生成无法被正确识别的“占位字符”。jQuery处理该字符串时,会自动用UTF-8编码这些字符,单个字节会被转成2-3个字节,直接导致数据量膨胀。- jQuery
$.post的默认编码行为:即便手动设置Content-Type为application/octet-stream,jQuery的AJAX方法默认仍会把数据当成application/x-www-form-urlencoded格式处理,对字符串进行编码,进一步增加数据体积。
正确解决方案
方案1:使用FormData(最简便,推荐)
FormData可直接封装文件对象,浏览器会自动处理二进制数据的编码和Content-Type设置,完全避免手动处理的问题:
var file = $("#file-to-upload")[0].files[0]; var url = "/upload"; var formData = new FormData(); formData.append('file', file); $.ajax({ url: url, type: 'POST', data: formData, processData: false, // 禁止jQuery自动处理数据格式 contentType: false, // 让浏览器自动生成包含boundary的Content-Type success: function(r) { console.log(r); }, error: function(xhr, status, error) { console.log(xhr); } });
方案2:使用ArrayBuffer直接发送二进制
如果必须手动设置Content-Disposition这类请求头,可以用readAsArrayBuffer读取文件,再通过原生XMLHttpRequest发送,绕开jQuery的自动编码:
var file = $("#file-to-upload")[0].files[0]; var filename = file.name; var url = "/upload"; const reader = new FileReader(); reader.onload = function(e) { console.log(`Read file data: length: ${e.target.result.byteLength}`); const xhr = new XMLHttpRequest(); xhr.open('POST', url); xhr.setRequestHeader("Content-Type", "application/octet-stream"); xhr.setRequestHeader("Content-Disposition", `attachment; filename=${filename}`); xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.responseText); } else { console.log(xhr); } }; xhr.send(e.target.result); // 直接发送ArrayBuffer二进制数据 } reader.readAsArrayBuffer(file);
内容的提问来源于stack exchange,提问作者Bart Friederichs
相关产品推荐
相关产品推荐

