AJAX. Загрузка файлов.
Немного странное название статьи… Мы помним, что 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 позволяет найти запчасть на автомобиль любой марки.
Приятный и удобный интернет магазин в котором есть все: от бытовой техники, до фототехники и телефонов. Захотел даже купить себе оригинальные весы подсказывающие цветом индикации динамику изменения моего веса.
В тему:
Спасибо вам большое за данный пост, но все ровно не догоняю.
Получается что form загружает на прямую в iframe.
Совершенно верно, атрибут 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Классное решение во всем интернете)
Nurya, September 21, 2010 2:18 pmСпасибо, с вашей помощью решил свою задачу)
Андрей, August 11, 2011 4:30 pmОписанный вами способ не работает в FF и Opera, третий день голову ломаю что это может быть… Может вы подскажете?
sima, September 2, 2011 2:33 pmИзвиняюсь, всё работает, чуть чуть провтыкал с вёрсткой 🙂
sima, September 4, 2011 4:58 pmОтличная статья, спасибо.
З.Ы. Совет: поменяйте цвет кнопок под формой для комментария =) Выделяются ппц как =)
Ага, это новый экспериментальный дизайн, мы его еще возможно подопиливаем.
Вячеслав Гринин, November 7, 2011 5:19 pmОднако, чего-то фалы не хотят грузиться
SNELS, November 4, 2011 10:36 pmА чего ж не грузятся-то?
Вячеслав Гринин, November 7, 2011 5:20 pmРешение очень простое, спасибо. Но есть вопросик как мне на страничку handler.php передать переменные то???
Александр, December 13, 2011 12:03 pmЛибо добавить в форму еще по одному INPUT type=”hidden” на каждую переменную, либо добавьте в свойство action формы GET-параметры, вот так: action=”handler.php?var1=value1&var2=value2″
Вячеслав Гринин, December 13, 2011 12:23 pmа если переменные находятся в AJAX?
Александр, December 13, 2011 12:40 pmЯ не понял, что значит ваш вопрос, но попробую предположить, что вы имеете в виду переменные, находящиеся в памяти? ну то есть, когда в 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Спасибо буду пробовать
Александр, December 13, 2011 1:46 pmbuttonClick is not defined
Александр, December 13, 2011 1:56 pmЕсли не сложно, пришлите мне исходник, как он есть у вас на vgrinin@gmail.com чтобы я посмотрел, где ошибка. Возможно при копировании кода из блога, у вас кавычки неправильные вставились.
Вячеслав Гринин, December 13, 2011 2:23 pmНе поможете ли мне с такой вот проблемой. Я передаю фото на сервер, будущие аватарки. Вашим способом, всё загружается, только вот вместо ifreme “картинка такая то загружена” нужно реализовать окошко в котором можно будет выбрать определенный кусок фотографии который будет в процессе отображатся на аве.
Николай, January 26, 2012 2:04 pmЕсли на словах, то вам нужно сделать следующее:
1) PHP-скрипт должен возвращать не filename, а url преобразованной картинки – аватарки
2) js-функцию onResponse в первом скрипте нужно модифицировать, чтобы она использовала присланный ей url для вставки элемента IMG в дерево документа с атрибутом src установленным равным присланному урлу картинки
Я шел именно по этой структуре, php возвращает путь до файла вместе с расширением, теперь у меня в окошке пишет “Файл ./папка/имяфайла.jpg загружен”. А вот как вместо этого окна вывести само изображение, подскажите пожалуйста.
Николай, January 30, 2012 7:52 amПопробуйте первый кусок кода изменить так.
<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:47 pmА что помешало загрузить изображение? Какие трудности и ошибки возникли?
Вячеслав Гринин, February 15, 2012 10:47 pmПочемуто не загружает файлы больше 2-х мегабайт, выдает alert(‘Файл … НЕ загружен.’);
Валерий, March 16, 2012 1:38 pmЭто настройки по умолчанию вашего сервера. Вот тут http://www.vikeng.info/max_file_size описана настройка этих параметров.
Вячеслав Гринин, March 16, 2012 8:10 pmСпасибо за статью! Коротко и предельно понятно + очень наглядный пример.
Gilean, March 24, 2012 2:40 amХорошая статья.
Может быть подскажите как сделатьть так, чтобы ответ от handler.php передовался если файл грузится на другой сервак (сайт, домен)?
Спасибо.
BorisBritva, March 28, 2012 6:15 pmДа, есть такая проблема, состоящая в том, что браузер запрещает обращаться к объекту window.parent, если вызывающий скрипт и объект parent находятся в разных доменах.Я точно не уверен, но думаю, что решить проблемцу поможет информация, изложенная в моей статье “Кросдоменная передача данных между html-страницами”(http://easy4web.ru/?p=836 ), только теперь мы будем бороться не с объектом opener, а с объектом parent. Попробуйте и расскажите,что получится.
Вячеслав Гринин, March 28, 2012 7:53 pmИнтересный способ. Только вот если handler.php поместить в папку folder и в action указать folder/handler.php, то работать не будет. Впрочем это очевидно, т.к. handler.php будет искать rFrame в папке folder.
denhell, April 7, 2012 10:46 amПацаны ваще ребята!
Четко =)
Искал долго как сделать пре показ изображения на mvc3 перед загрузкой.
После прочтения твоей статьи сделал за 3 секунды!
Рад, что смог помочь.
Вячеслав Гринин, April 8, 2012 9:44 pmВячеслав! Добрый день! Вы не можете подсказать, как стандартную форму заменить на какую-нибудь красивую самодельную кнопку?
Александр, June 24, 2012 6:01 pmВот тут посмотрите http://easy4web.ru/?p=41
Вячеслав Гринин, June 25, 2012 7:28 amВы меня немножко неправильно поняли. Или я невнятно выразился. Я хотел бы заменить именно этот импут для загрузки файлов на что-нибудь более красивое и интересное
Александр, June 25, 2012 8:44 amизвините, может быть Я Вам надоедаю, но у меня есть еще один вопрос. Почему, если в обработчик я вношу код, касающийся занесения в базу данных пути к сохраненной картинки, скрипт не выполняется. Какие то конфликты что ли возникают? Разъясните пожалуйста. С уважением Александр
Александр, June 25, 2012 5:00 pmблин!! Нашел ошибку в коде. Все грузится. Спасибо Вам огромное за этот продукт!!!!!!!!!!!!!!!
Александр, June 25, 2012 5:04 pmНе подскажете, почему в IE не хочет работать скрипт?
Александр, July 6, 2012 8:33 amХорошая статья. Автору плюс. Есть один вопрос, можно обойтись без кнопки Submit? Я как понимаю нужно добавить в объект формы file событие, что-то типа . Как сделать чтобы при выборе файла, он сразу же стал загружаться?
Владимир, November 28, 2012 3:37 pmЗдраствуйте! Подскажите, почему выбивает ошибку когда я выбираю загрузить файл. Ошибка ввида (алерт) – Файл имя файла.png НЕ загружен. Проверяю на локалном сервере Денвер. Буду благодарен за ответ!
Дмитрий, October 29, 2013 10:06 amперепробовал все браузеры на локалхосте – ничего не работает. подскажите что не так?
Игорь, December 4, 2014 6:23 pmа где можно детально почитать про эту технология, че то не понятно, функция onresponse на главной странице принимает объект, а кто ей передает объект? iframe что ли? можно ли подробно спросить про описание? где можно почитать про это?
Дулат, February 24, 2016 3:53 pmСпасибо Вячеслав за хорошие примеры!
Подскажите, а можно ли сделать для данного кода progress bar, то есть просто цветную полоску, которая показывает загрузку файла?