不幸的是,如果没有一些中间编码,这在 JavaScript 中是不可能的。为了理解原因,我们假设您对数据进行了 Base64 解码并发布,就像您在示例中所描述的那样。有效 PHP 文件的前几行(十六进制)可能如下所示:
0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
0000010: 0000 0080 0000 0080 0806 0000 00c3 3e61 ..............>a
如果您查看上传的 PNG 文件的相同范围的十六进制,它将如下所示:
0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
0000010: 0000 00c2 8000 0000 c280 0806 0000 00c3 ................
差异是微妙的。比较第二行的第二列和第三列。在有效文件中,这四个字节是0x00
0x80
0x00
0x00
。在您上传的文件中,相同的四个字节是0x00
0xc2
0x80
0x00
. Why?
JavaScript 字符串是 UTF。这意味着任何 ASCII 二进制值 (0-127) 都用一个字节进行编码。然而,从 128 到 2047 之间的任何数字都可以two字节。那个额外的0xc2
上传的文件中是这种多字节编码的产物。如果你想确切地知道为什么会发生这种情况,你可以阅读更多关于维基百科上的 UTF 编码 http://en.wikipedia.org/wiki/UTF-8#Description.
您无法阻止 JavaScript 字符串发生这种情况,因此您无法在不使用 base64 的情况下通过 AJAX 上传此二进制数据。
EDIT:经过进一步挖掘,某些现代浏览器可以实现这一点。如果浏览器支持XMLHttpRequest.prototype.sendAsBinary
(Firefox 3 和 4),您可以使用它来发送图像,如下所示:
function postCanvasToURL(url, name, fn, canvas, type) {
var data = canvas.toDataURL(type);
data = data.replace('data:' + type + ';base64,', '');
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
var boundary = 'ohaiimaboundary';
xhr.setRequestHeader(
'Content-Type', 'multipart/form-data; boundary=' + boundary);
xhr.sendAsBinary([
'--' + boundary,
'Content-Disposition: form-data; name="' + name + '"; filename="' + fn + '"',
'Content-Type: ' + type,
'',
atob(data),
'--' + boundary + '--'
].join('\r\n'));
}
对于没有的浏览器sendAsBinary
,但确实有Uint8Array
(Chrome 和 WebKit),你可以像这样填充它:
if (XMLHttpRequest.prototype.sendAsBinary === undefined) {
XMLHttpRequest.prototype.sendAsBinary = function(string) {
var bytes = Array.prototype.map.call(string, function(c) {
return c.charCodeAt(0) & 0xff;
});
this.send(new Uint8Array(bytes).buffer);
};
}