З реалізацією браузерами специфікації HTML5 багато речей в веб стало робити набагато простіше і приємніше. Одна з таких речей - ajax-запити в загальному і завантаження файлів на сервер за допомогою ajax зокрема.
Отже, завантажуємо файл на сервер без перезавантаження сторінки - версія з мінімальною кількістю коду. Використовуємо jQuery і розглядаємо тільки клієнтську частину (без серверної поки).
На роботі ми зараз розробляємо свою CRM-систему. Backend пишемо на Yii. Особливістю роботи сайту є те, що при переході на іншу сторінку перехоплюється подія кліка по посиланню, відправляється запит на сервер і, отримавши у відповідь лише потрібний блок html-коду, перемальовує DOM-структуру. В результаті всі задоволені: клієнти - швидкою роботою сайту, адміни - зменшеним навантаженням на сервер, програмісти ж просто радіють красі рішення.
З'ясувалося, що з цим все добре, дякую специфікації html5. Головний недолік для production-проектів - підтримка IE10 +. Але ми вирішили поки не звертати увагу на користувачів з IE8,9 - якщо за статистикою після запуску проекту побачимо, що таких користувачів не так вже й мало, тоді зробимо рішення і під IE8,9.
Спочатку натрапив на плагін Jquery Ajax Upload. Начебто хороший, гарненький, але, як і всі фреймворки, тягне з собою багато js і css-файлів, а так само змушує, замість написання коду, писати правильні конфіги, що нітрохи не приємніше.
Я ж вирішив реалізувати все на jQuery (в проекті він все одно використовується).
Для початку будемо робити все якомога простіше. Html-розмітка буде дуже проста
Це код з backend'а, формуємо звичайний input з type = file, в data-url вказуємо URL, за яким будемо завантажувати картинку, ну і name містить назву input'a.
На клієнті у нас буде сформовано наступне
Поїхали далі - js.
Тут все максимально просто:
Так само, для кращого розуміння роботи скрипта, я спеціально залишив console.log (this.files); - можна подивитися, яка структура даних за замовчуванням створюється для html-елемента c type = file.
Але і тут я встиг зіткнутися з нюансом. Не дарма я вказав опцію processData: false. Без неї нічого не вийшло, в консолі браузера видавалася помилка: Uncaught TypeError: Illegal invocation в Chrome і більш осмислена TypeError: 'append' called on an object that does not implement interface FormData. в Firefox (остання кілер-фича Firebug'а якраз і полягає в тому, що помилки він виводить набагато більш осмислені. Найчастіше рятує.).
А все тому, що при proccessData = true jquery намагається конвертувати всі ajax-дані в один рядок. Природно, для файлу це не потрібно, по-цьому установкою даної опції в false ми позбавимо jQuery від мук.
Рішенням, як ви здогадалися, є установка параметра processData в false. Тепер все запрацювало - в chrome's developer tools на вкладці Network можна побачити, що запит успішно відправився з даними файлу. Відмінно!
Сервер поки повертає 404 помилку. Воно і зрозуміло - я їм взагалі ще не займався, ось він і образився.
серверна частина
На сервері, після настройки rout'ов і додаванні action'а в контролер (це в кожному фреймворку робиться по-різному), у нас з'являється доступ до завантаженого файлу через глобальний масив $ _FILES. Мало того, php вже завантажив файл на сам сервері в папку tmp. Тепер можна робити з ним все, що душа забажає. Але обмежуються зазвичай лише переміщенням файлу в більш зручну директорію (tmp має властивість очищатися) і, можливо, додаванню записи в базу даних.
Власне, за нескладною команді var_dump ($ _ FILES) у мене вийшов ось такий висновок:
Бінго! Файл успішно завантажений на сервер за допомогою Ajax.