Заглавието и на мен не ми говори нищо, за това ще се опитам да обясня с прости думи казуса.
Наскоро забелязах, че при изпращането на голям скрийншот в http://screenshoot.me се случват някакви неприятни неща. По-точно грешка 413 Request entity too large. Оказа се, че хостингът ми не позволява изпращане на POST заявки с параметри по-големи от 1-2мб. За сметка на това пък позволява качване на файлове до 20мб. До тук единственото нещо, което ме притесняваше е дали може да „симулирам“ изпращане на файл чрез AJAX. Оказа се че може. FUCK YEAH!
Всъщност може да си префасонирате цялата заявка както ви скимне, което е много готино и същевремено много жалко, защото десетките хиляди фронт-енд дивелъпъри, ползващи jQuery и нямащи понятие от javascript никога няма да узнаят за този факт.
Вероятно бъркам, просто се опитвам да си обясня защо на всяко интервю винаги питат „а ти с jQuery знаеш ли как да работиш“ все едно манюъла е 500 страници и ти трябва висше
.
Та реших да разширя леката библиотечка („парче код“ е правилната дума), която ползвам за AJAX обаждания и съответно резултата е тук – https://github.com/lucho870601/ajax-blob-upload
Накратко:
jx.generateBoundary = function(fieldsData) { // Функция, която генерира уникален разделител валиден за всеки фрагмент
var boundary = parseInt(Math.random()*Math.pow(10, 16)).toString(36) + '' + parseInt(Math.random()*Math.pow(10, 16)).toString(36);
for (var i = 0; i < fieldsData.length; i++) {
if (fieldsData[i].indexOf(boundary) > -1) {
// generate new boundary and check all fields again
boundary = parseInt(Math.random()*Math.pow(10, 16)).toString(36) + '' + parseInt(Math.random()*Math.pow(10, 16)).toString(36);
i = 0;
}
}
return boundary;
};
jx.loadFile = function(url, fileData, fileName, callback, opt) {
var http = this.init(); //The XMLHttpRequest object is recreated at every call - to defeat Cache problem in IE
if(!http||!url) return;
var parts = url.split('?');
var url = parts[0];
var parameters = parts[1] ? parts[1].split('&') : [];
var fieldsData = [fileData];
for (var i = 0; i < parameters.length; i++) {
fieldsData.push(parameter[i][1]);
}
var boundary = this.generateBoundary(fieldsData);
var body = '';
for (var i = 0; i < parameters.length; i++) {
// строим фрагментите за данни
var p = parameters[i].split('=');
body += "--" + boundary + "\r\n\
Content-Disposition: form-data; name='"+p[0]+"'\r\n\
\r\n\
"+(p[1] || '')+"\r\n";
}
// фрагмента за псевдо-файла
body += "--" + boundary + "\r\n\
Content-Disposition: form-data; name='" + fileName + "'; filename='" + fileName + "'\r\n\
Content-Type: application/octet-stream\r\n\
\r\n\
"+ fileData + "\r\n\
--" + boundary + "--\r\n";
http.open("POST", url, true);
http.setRequestHeader("Content-Type", "multipart/form-data; boundary="+boundary);
http.setRequestHeader("Content-Length", body.length);
http.setRequestHeader("Connection", "close");
...
http.send(body);
};
… и получавате данните си като файл накрая.
Кофтито в цялата история е, че не мога да убедя браузъра ми да не слага „charset“ и вероятно заради това blob-а пристига в съвсем друг вид. За това пък решението е доста лесно – кодиране на бинарните данни в base64. Неприятно е, че request-а ще набъбне с 1/3 повече и ще трябва да отворите и decode-нете файла сами. На теория декодирането на файла трябва да стане автоматично с Content-Transfer-Encoding директива, но на практика не стана
Удачно е да ползвате този подход не само при гореописания проблем, но и винаги, когато пращате индустриални количества бинарни данни, защото получаването на файл е по-бързо и ангажира по-малко ресурси отколкото получаването на данните като параметър. Поне така ми се струва

П.П. Преди да имплементирам алтернативното решение се помъчих да увелича лимита на размера на POST заявките с конфигуриране на .htaccess и ini_set на разни магически PHP параметри, но непотръгна.
Тагове: ajax, blob, Javascript