AJAX. Загрузка файлов.

Вячеслав Гринин, February 20, 2010

Немного странное название статьи… Мы помним, что AJAX это асинхронный JavaScript, построенный на основе объекта XmlHttpRequest. Отличная технология! С некоторыми ограничениями. Одно из них – отсутствие кросдоменности – мы уже научились обходить в статье Кросдоменный JavaScript (JSONP). Но есть у этой замечательной технологии еще один недостаток – она не позволяет асинхронно загружать файлы. Ну вот не умеет объект XmlHttpRequest загружать файлы!

Как обойти это ограничение? Как оказалось – очень просто. Правда это уже не будет AJAX, но это все еще будет асинхронный запрос, то есть при загрузке файла нам не придется перезагружать саму страницу с формой, загрузка произойдет в фоновом режиме. Да еще и вернет нам результат операции в виде произвольного объекта, полностью описывающего результат загрузки. Это очень хорошо.

Для загрузки файлов мы используем технологию, которая называется Remote Scripting посредством IFRAME. А в общем, технология проста. Для загрузки файла мы все также будем использовать обычную форму FORM, в которой разместим самый обычный INPUT TYPE=’FILE’. Все как и прежде в случае синхронной загрузки файла. И вот здесь и начинаются отличия. А отличие состоит в том, что у формы target указывает на скрытый на странице IFRAME, который по сути и перезагрузится во время операции. Мало того, в IFRAME мы вернем результат операции в виде вызова простого JavaScript-коллбэка, который завершит операцию загрузки.

Итак, от слов к делу. Далее я приведу пример кода, который и реализует вышеописанную технологию. Код этот очень простой.

<html>
<head>
<script type="text/javascript">
 function onResponse(d) {
 eval('var obj = ' + d + ';');
 alert('Файл ' + obj.filename + (obj.success ? " " : " НЕ ") + 
    "загружен.");
 }
 </script>
</head>

<iframe id="rFrame" name="rFrame" style="display: none">
</iframe>

<form action="handler.php" target="rFrame" method="POST" 
   enctype="multipart/form-data">
<input type="file" name="loadfile">
<input type='submit' value='Загрузить'>
</form>

</html>

Здесь мы видим функцию onResponse, которая получает результат обработки файла(результат этот мы формируем на сервере). Еще мы видим IFRAME с идентификатором rFrame, про который мы говорили выше. И видим мы также саму форму загрузки файла.

У формы action=”handler.php” указывает на серверный обработчик, и не забудьте указать enctype=”multipart/form-data” иначе форма не сможет загружать файлы.

А дальше я приведу серверный код, содержимое файла handler.php.

<?php
 function jsOnResponse($obj)
 {
 echo '
 <script type="text/javascript">
 window.parent.onResponse("'.$obj.'");
 </script>
 ';
 }

 $dir = '/home/path/path/path';
 $name = basename($_FILES['loadfile']['name']);
 $file = $dir . $name;

 $success = move_uploaded_file($_FILES['loadfile']['tmp_name'], $file);
 jsOnResponse("{'filename':'" . $name . "', 'success':'" . $success . "'}");

?>

Этот код просто сохраняет файл в папку на сервере, объяснять саму загрузку нет смысла – она стандартна. Обращу лишь внимание, что после загрузки файла вызывается функция jsOnResponse которая просто пишет в выходной поток вызов JavaScript-функции window.parent.onResponse($obj). Клиентский IFRAME, приняв этот код, выполнит его, вызвав функцию onResponse у родительской страницы фрейма.

Вот и все. Пользуйтесь. Здесь можно скачать работающий код приведенного в статье примера.

Используя уникальную систему поиска Price-AZ можно заказать любые запчасти Фольксваген и Ауди на сайте ООО “Польга”. Теперь стало гораздо удобнее найти нужную запчасть не только по номеру, но и по ее названию. При магазине есть также удобный автосервис. Кстати, система Price-AZ позволяет найти запчасть на автомобиль любой марки.

Приятный и удобный интернет магазин в котором есть все: от бытовой техники, до фототехники и телефонов. Захотел даже купить себе оригинальные весы подсказывающие цветом индикации динамику изменения моего веса.

В тему:

41комментарий

Спасибо вам большое за данный пост, но все ровно не догоняю.
Получается что form загружает на прямую в iframe.

kos6476, September 14, 2010 8:58 am Reply

Совершенно верно, атрибут target как раз для того и предназначен, чтобы указать в какой IFRAME загружать ответ формы (или кликнутой ссылки, потому что у элемента A тоже есть атрибут target). Кроме имени IFRAME’а в атрибут taget можно написать одно из нескольких предопределенных значений: _blank, _self, _parent, _top. Прэ них вы можете подробнее прочитать, например, вот здесь http://www.w3schools.com/tags/att_a_target.asp (правда, по-английски).

admin, September 14, 2010 9:08 am Reply

Классное решение во всем интернете)

Nurya, September 21, 2010 2:18 pm Reply

Спасибо, с вашей помощью решил свою задачу)

Андрей, August 11, 2011 4:30 pm Reply

Описанный вами способ не работает в FF и Opera, третий день голову ломаю что это может быть… Может вы подскажете?

sima, September 2, 2011 2:33 pm Reply

Извиняюсь, всё работает, чуть чуть провтыкал с вёрсткой :)

sima, September 4, 2011 4:58 pm Reply

Отличная статья, спасибо.
З.Ы. Совет: поменяйте цвет кнопок под формой для комментария =) Выделяются ппц как =)

SNELS, November 4, 2011 10:24 pm Reply

Ага, это новый экспериментальный дизайн, мы его еще возможно подопиливаем.

Вячеслав Гринин, November 7, 2011 5:19 pm Reply

Однако, чего-то фалы не хотят грузиться

SNELS, November 4, 2011 10:36 pm Reply

А чего ж не грузятся-то?

Вячеслав Гринин, November 7, 2011 5:20 pm Reply

Решение очень простое, спасибо. Но есть вопросик как мне на страничку handler.php передать переменные то???

Александр, December 13, 2011 12:03 pm Reply

Либо добавить в форму еще по одному INPUT type=”hidden” на каждую переменную, либо добавьте в свойство action формы GET-параметры, вот так: action=”handler.php?var1=value1&var2=value2″

Вячеслав Гринин, December 13, 2011 12:23 pm Reply

а если переменные находятся в AJAX?

Александр, December 13, 2011 12:40 pm Reply

Я не понял, что значит ваш вопрос, но попробую предположить, что вы имеете в виду переменные, находящиеся в памяти? ну то есть, когда в JavaScript у вас есть var a=”что-то”, и эту переменную вам надо передать в handler.php. Если да, то можно поступить так.
1) Добавляем на форму несколько инпутов type=”hidden” по количеству передаваемых переменных. Присваиваем каждому из них уникальный id, например param1, param2.
2) Задаем форме id=”form1″ (например)
3) Заменяем submit-кнопку на обычную
4) Ставим у кнопки атрибут onclick=”buttonClick()”
5) В скрипте на той же странице, где форма добавляем обработчик function buttonClick() {
document.getElementById(“param1″).value = a; // та самая переменная a
// задаем таким же образом остальные передаваемые аргументы
…..
// передаем форму на сервер
document.getElementById(“form1″).submit();
}

На сервере принимаем все эти аргументы в объект $_POST, например $a = $_POST["param1"]

Вячеслав Гринин, December 13, 2011 1:18 pm Reply

Спасибо буду пробовать

Александр, December 13, 2011 1:46 pm

buttonClick is not defined

Александр, December 13, 2011 1:56 pm

Если не сложно, пришлите мне исходник, как он есть у вас на vgrinin@gmail.com чтобы я посмотрел, где ошибка. Возможно при копировании кода из блога, у вас кавычки неправильные вставились.

Вячеслав Гринин, December 13, 2011 2:23 pm Reply

Не поможете ли мне с такой вот проблемой. Я передаю фото на сервер, будущие аватарки. Вашим способом, всё загружается, только вот вместо ifreme “картинка такая то загружена” нужно реализовать окошко в котором можно будет выбрать определенный кусок фотографии который будет в процессе отображатся на аве.

Николай, January 26, 2012 2:04 pm Reply

Если на словах, то вам нужно сделать следующее:
1) PHP-скрипт должен возвращать не filename, а url преобразованной картинки – аватарки
2) js-функцию onResponse в первом скрипте нужно модифицировать, чтобы она использовала присланный ей url для вставки элемента IMG в дерево документа с атрибутом src установленным равным присланному урлу картинки

Вячеслав Гринин, January 27, 2012 10:30 am Reply

Я шел именно по этой структуре, php возвращает путь до файла вместе с расширением, теперь у меня в окошке пишет “Файл ./папка/имяфайла.jpg загружен”. А вот как вместо этого окна вывести само изображение, подскажите пожалуйста.

Николай, January 30, 2012 7:52 am Reply

Попробуйте первый кусок кода изменить так.


<html>
<head>
<script type="text/javascript">
 function onResponse(d) {
   eval('var obj = ' + d + ';');
   var ava = document.getElementById("ava_img");
   if(ava){
     ava.src = obj.filename;
     ava.style.display = "block";
   }
 }
 </script>
</head>

<img src="" style="display: none;" id="ava_img"/>

<iframe id="rFrame" name="rFrame" style="display: none">
</iframe>

<form action="handler.php" target="rFrame" method="POST"
   enctype="multipart/form-data">
<input type="file" name="loadfile"/>
<input type='submit' value='Загрузить'/>
</form>

</html>
Вячеслав Гринин, January 30, 2012 10:03 am

Большое спасибо за статью!
Пытался написать форму с занесением всех данных, в том числе и аватарки, в базу данных. Всё получилось выполнить с помощью JavaScript без перезагрузки страницы, но изображение не смог передать. Это возможно или нет ?

Роголев, February 15, 2012 10:31 pm Reply

Ну конечно. Статья ж как раз и описывает как загружать файлы, и изображения в том числе.

Вячеслав Гринин, February 15, 2012 10:47 pm Reply

А что помешало загрузить изображение? Какие трудности и ошибки возникли?

Вячеслав Гринин, February 15, 2012 10:47 pm Reply

Почемуто не загружает файлы больше 2-х мегабайт, выдает alert(‘Файл … НЕ загружен.’);

Валерий, March 16, 2012 1:38 pm Reply

Это настройки по умолчанию вашего сервера. Вот тут http://www.vikeng.info/max_file_size описана настройка этих параметров.

Вячеслав Гринин, March 16, 2012 8:10 pm Reply

Спасибо за статью! Коротко и предельно понятно + очень наглядный пример.

Gilean, March 24, 2012 2:40 am Reply

Хорошая статья.

Может быть подскажите как сделатьть так, чтобы ответ от handler.php передовался если файл грузится на другой сервак (сайт, домен)?

Спасибо.

BorisBritva, March 28, 2012 6:15 pm Reply

Да, есть такая проблема, состоящая в том, что браузер запрещает обращаться к объекту window.parent, если вызывающий скрипт и объект parent находятся в разных доменах.Я точно не уверен, но думаю, что решить проблемцу поможет информация, изложенная в моей статье “Кросдоменная передача данных между html-страницами”(http://easy4web.ru/?p=836 ), только теперь мы будем бороться не с объектом opener, а с объектом parent. Попробуйте и расскажите,что получится.

Вячеслав Гринин, March 28, 2012 7:53 pm Reply

Интересный способ. Только вот если handler.php поместить в папку folder и в action указать folder/handler.php, то работать не будет. Впрочем это очевидно, т.к. handler.php будет искать rFrame в папке folder.

denhell, April 7, 2012 10:46 am Reply

Пацаны ваще ребята!
Четко =)
Искал долго как сделать пре показ изображения на mvc3 перед загрузкой.
После прочтения твоей статьи сделал за 3 секунды!

Роман, April 7, 2012 3:33 pm Reply

Рад, что смог помочь.

Вячеслав Гринин, April 8, 2012 9:44 pm Reply

Вячеслав! Добрый день! Вы не можете подсказать, как стандартную форму заменить на какую-нибудь красивую самодельную кнопку?

Александр, June 24, 2012 6:01 pm Reply

Вот тут посмотрите http://easy4web.ru/?p=41

Вячеслав Гринин, June 25, 2012 7:28 am Reply

Вы меня немножко неправильно поняли. Или я невнятно выразился. Я хотел бы заменить именно этот импут для загрузки файлов на что-нибудь более красивое и интересное

Александр, June 25, 2012 8:44 am Reply

извините, может быть Я Вам надоедаю, но у меня есть еще один вопрос. Почему, если в обработчик я вношу код, касающийся занесения в базу данных пути к сохраненной картинки, скрипт не выполняется. Какие то конфликты что ли возникают? Разъясните пожалуйста. С уважением Александр

Александр, June 25, 2012 5:00 pm Reply

блин!! Нашел ошибку в коде. Все грузится. Спасибо Вам огромное за этот продукт!!!!!!!!!!!!!!!

Александр, June 25, 2012 5:04 pm Reply

Не подскажете, почему в IE не хочет работать скрипт?

Александр, July 6, 2012 8:33 am Reply

Хорошая статья. Автору плюс. Есть один вопрос, можно обойтись без кнопки Submit? Я как понимаю нужно добавить в объект формы file событие, что-то типа . Как сделать чтобы при выборе файла, он сразу же стал загружаться?

Владимир, November 28, 2012 3:37 pm Reply

Здраствуйте! Подскажите, почему выбивает ошибку когда я выбираю загрузить файл. Ошибка ввида (алерт) – Файл имя файла.png НЕ загружен. Проверяю на локалном сервере Денвер. Буду благодарен за ответ!

Дмитрий, October 29, 2013 10:06 am Reply

перепробовал все браузеры на локалхосте – ничего не работает. подскажите что не так?

Игорь, December 4, 2014 6:23 pm Reply
Ваше имя
Ваш email*
Ваш сайт
Текст вашего комментария:

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: