Отправка изображений из элементов Canvas с помощью Ajax и PHP $ файлов
мне нужно иметь возможность отправлять изображение и некоторые поля формы из элемента canvas на стороне клиента в php-скрипт, заканчивающийся в $_POST и $_FILES. Когда я посылаю его так:
<script type="text/javascript">
var img = canvas.toDataURL("image/png");
...
ajax.setRequestHeader('Content-Type', "multipart/form-data; boundary=" + boundary_str);
var request_body = boundary + 'n'
+ 'Content-Disposition: form-data; name="formfield"' + 'n'
+ 'n'
+ formfield + 'n'
+ 'n'
+ boundary + 'n'
+ 'Content-Disposition: form-data; name="async-upload"; filename="'
+ "ajax_test64_2.png" + '"' + 'n'
+ 'Content-Type: image/png' + 'n'
+ 'n'
+ img
+ 'n'
+ boundary;
ajax.send(request_body);
</script>
$_POST и $_FILES оба возвращаются заполненными, но данные изображения в $_FILES все еще нуждаются в декодировании следующим образом:
$loc = $_FILES['async-upload']['tmp_name'];
$file = fopen($loc, 'rb');
$contents = fread($file, filesize($loc));
fclose($file);
$filteredData=substr($contents, strpos($contents, ",")+1);
$unencodedData=base64_decode($filteredData);
...чтобы сохранить его как читаемый PNG. Это не вариант, поскольку я пытаюсь передать изображение в функцию media_handle_upload () Wordpress, которая требует индекс $_FILES, указывающий на читаемое изображение. Я также не могу декодировать, сохранять и изменять "tmp_name" соответственно, так как это нарушает проверки безопасности.
Итак, я нашел это: http://www.webtoolkit.info/javascript-base64.html и попытался сделать декодирование на стороне клиента:
img_split = img.split(",",2)[1];
img_decoded = Base64.decode( img_split );
но по какой-то причине я все еще не получаю читаемый файл, когда он попадает в PHP. Поэтому возникает вопрос: "почему?"или "что я делаю не так?"или "это вообще возможно?" :-)
любая помощь очень высоко ценится!
спасибо, Кейн!--4-->
2 ответов
основываясь на отличном ответе Натана, я смог finnagle его так, что он все еще проходит через jQuery.Аякс. Просто добавьте это в запрос ajax:
xhr: function () {
var myXHR = new XMLHttpRequest();
if (myXHR.sendAsBinary == undefined) {
myXHR.legacySend = myXHR.send;
myXHR.sendAsBinary = function (string) {
var bytes = Array.prototype.map.call(string, function (c) {
return c.charCodeAt(0) & 0xff;
});
this.legacySend(new Uint8Array(bytes).buffer);
};
}
myXHR.send = myXHR.sendAsBinary;
return myXHR;
},
в принципе, вы просто возвращаете объект xhr, который переопределен, так что" Отправить "означает"sendAsBinary". Тогда jQuery поступает правильно.
к сожалению, это невозможно в JavaScript без некоторой промежуточной кодировки. Чтобы понять, почему, предположим, вы base64 декодировали и разместили данные, как вы описали в своем примере. Первые несколько строк в hex допустимого файла 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
. Почему?
строки JavaScript являются UTF. Это означает, что любые двоичные значения ASCII (0-127) кодируются одним байтом. Однако, ничего из 128-2047 получает два байт. Это лишнее 0xc2
в загруженном файле находится артефакт этой многобайтовой кодировки. Если вы хотите точно знать, почему это происходит, вы подробнее о кодировка UTF в Википедии.
вы не можете предотвратить это с помощью строк JavaScript, поэтому вы не можете загружать эти двоичные данные через AJAX без использования base64.
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);
};
}