Загрузка файлов средствами AJAX

Прежде всего, нужно сразу сказать, что отправить (загрузить) на сервер файл с помощью AJAX нельзя, поэтому приходится прибегать к некоторым хитростями, а конкретно - использовать скрытый фрейм iframe.

Итак, мы создаем скрытый фрейм iframe, и в атрибуте target тега form указываем имя этого фрейма. Отправка файла будет выполнена обычным способом. Но с точки зрения посетителя загрузка будет выглядеть асинхронной - страница, которую он видит, перезагружена не будет.

<form action="/upload.php" name="uploadForm" method="post" target="hiddenframe" enctype="multipart/form-data"
onsubmit="document.getElementById('res').innerHTML=''; document.getElementById('loading').style.display='block';
return true;"
>

<input type="file" name="userfile" />
<input type="submit" value="Загрузить"  />
</form>
<div id="res" style="margin: 1em 0"></div>
<div id="loading" style="display:none; position: absolute; z-index: 99; left: 45%; top: 45%;">
<img src="/loading.gif" border="0" /> Идет загрузка...
</div>
<iframe id="hiddenframe" name="hiddenframe" style="width:0; height:0; border:0"></iframe>

Работает это так - при нажатии на кнопку submit на экране появляется изображение “loading” (загрузка), потом идет отправка формы. Когда файл загрузился, мы убираем изображение загрузки и выводим на основную страницу сообщение с результатами.

<?php
// ...............................................
sleep(5);
echo '<script type="text/javascript">';
echo 'window.parent.document.getElementById("loading").style.display="none";';
// Если загрузка прошла успешно
if (empty($error)) {
   echo 'window.parent.document.getElementById("res").innerHTML="Файл успешно загружен";';
} else {
  echo 'window.parent.document.getElementById("res").innerHTML="Ошибка при загрузке файла";';
}
echo '</script>';
?>

Серверный скрипт upload.php создает текстовую строку, содержащую обычный JavaScript код. Когда ответ сервера будет загружен во фрейм, код будет автоматически выполнен.

Комментариев: 30

  1. valenok:

    Слово ajax, как и сама технология к статье отношений не имеет. Просто отправка файла без перезагрузки страницы.

  2. admin:

    valenok, справедливое замечание: заметку стило бы озаглавить “Асинхронная загрузка файлов” или что-то в этом духе.

  3. danko-13:

    Абсолютно согласен с предыдущим комментатором. Вообще, блог - полное г… Посмотрел на 3 рандомно на 3 странички - пипец!!!
    Более-менее знающий кодер здесь ничего нового не найдет, а новичкам тем более непонятно ничего. Вспомни себя, автор, в самом начале изучения Web-технологий и ответь: Что реально полезного дает твой блог?

  4. caesar:

    То что говорит danko-13 полный бред, определенные интересные вещи в блоге есть. И надо иметь ввиду что блог делается не для всех подряд, а в основном для автора) Если автор удовлетворен и ему все нравится значит он все делает правильно. Если его труд пригодится кому то еще вдвойне приятно. Если для тебя нет ни чего полезного можно не читать и обратится к другим источникам.

  5. Александр:

    нормально! я вот уже не начинающий но еще не профи…есть что интиресного.

  6. Лексеич:

    Автору спасибо, кое-что вынес для себя ;)

    danko-13: убей себя об стену и схорони останки :P

  7. Прим Палвер:

    Если у браузера яваскрипт отключен, как будет вести себя субмит? Перезагрузит страницу или проигнорирует нажатие?

  8. admin:

    Прим Палвер, думаю, файл будет загружен, только надписи “Идет загрузка” и сообщения о результатате загрузки мы не увидим. Потому как это делается средствами JavaScript.

  9. Вадим:

    В данном случие AJAX не используется

  10. frf:

    А куда файл загружается?

  11. admin:

    frf, туда же, куда всегда - во временную директорию, определяемую директивой upoad_tmp_dir файла php.ini:
    Загрузка файлов на сервер
    Чтобы сохранить этот файл мы должны его скопировать куда-нибудь, потому что после окончания работы скрипта файл в директории upoad_tmp_dir будет автоматически удален.

  12. Pepper:

    Попробуйте пропустить такой код через HTML валидатор, установив doctype XHTML 1.0 Strict.

    Валидатор ругнется на аттрибут target и на наличие фреймов. Прием работающий, но корявый.

    Обход данной проблемы: создавайте iframe динамически. Если уж используете мощь JavaScript, то ипользуйте ее во всю, а не только для украшения дома на неровном фундаменте.

  13. admin:

    Pepper, вы не находите, что предложенный вами способ тоже несколько корявый? Допустим, мы создадим валидный XHTML-документ, а потом с помощью JavaScript добавим iframe, тем самым сделав его невалидым. Получается, решение проблемы в том, чтобы обмануть валидатор?

  14. Pepper:

    Нет, я не считаю этот способ корявым, ничуть.
    Если использовать предложенную мной идею, боты поисковиков не будут спотыкаться на вставленных в HTML iframe’ах, так как они игнорируют JavaScript.

    Я предпочитаю “обмануть” валидатор в случаях, где другого выхода просто нет. А это именно такой случай. Если уж мудрите с “ajax” загрузкой, то можно сделать это и красиво.

  15. Aube:

    Статья названа правильно - AJAX это не только XMLHttpRequest. Хоть вики почитайте, прежде чем пороть чушь.

    А валидность документов вообще ерунда - к примеру проверьте гугл, яндекс на валидность :) И тем более не стоит усложнять логику страницы скриптами для того чтобы соответствовать рекоменованным, но вовсе не обязательным стандартам.

    Автору за статью спасибо - помогла.

  16. Pepper:

    Aube, есть право выбора. Лепите страницы как хочется, кто ж не мешает.
    Только стоит подумать о кроссбраузерности прежде.
    Стандарты они для того и стандарты, хоть и не на 100% поддерживаются всеми браузерами.

  17. Красун:

    Технология называется comet. Но в силу привычку, мы называем подобные вещи и решения AJaX.

  18. Zoomanoid:

    Отличный способ обойти проблему перезагрузки страницы у пользователя при применеия Ajax и
    загрузить файл стандартным образом. А есть ли пример загрузки файла с применением XMLHttpRequest и есть ли там скрытые проблемы?

    Автору за статью спасибо.
    А за готовность вести такой блог - большое спасибо.

  19. admin:

    Zoomanoid, в статье XMLHTTPRequest: описание, применение, частые проблемы я встретил вот такие слова:
    Возможности XmlHttpRequest позволяют создать запрос с любым телом. Например, можно вручную сделать POST-запрос, загружающий на сервер файл. Функционал создания таких запросов есть, в частности, во фреймворке dojo. Но можно реализовать его и самому, прочитав о нужном формате тела POST и заголовках.
    Но сам я такого не делал - так что рассказать не смогу. И этому есть причины. Насколько я понимаю, загрузить файл с применением только XMLHttpRequest невозможно: в JavaScript отсутствует возможность прямой работы с локальными файлами. Т.е., сделать сам POST с помощью XMLHTTPRequest можно, но вот считать локальный файл в какую-нибудь переменную JavaScript - нет. Это сделано с целью обеспечения безопасности.

  20. Zoomanoid:

    Спасибо за ясность с локальными файлами.
    Не смог приспособить Ваш вполне рабочий пример для своих нужд, так как не смог с его помощью организовать обработку результатов выгрузки.
    Благодаря Вашему блогу нашел рабочее (после мелких исправлений) решение своей проблемы с
    использованием iframe. http://web-tec.info/

  21. SorokinWS:

    Большое спасибо, за интересную идею. В моём случае, это действительно оказалось выходом. Блог в целом не смотрел, но если тут много подобных штук, то буду захаживать.

  22. Константин:

    valenok, тем не меннее все известные AJAX библиотеки по такому принципу и работают. Нужно запомнить что НЕТ ВОЗМОЖНОСТИ С ПОМОЩЬЮ JAVA SCRIPT ОТПРАВИТЬ ФАЙЛ. Слово AJAX сдесь я думаю применимо. Т.к. с помощью Java Script возвращается результат.

  23. Роман:

    Очень полезная статья оказалась. И зря на нее гнали вначале - достаточно просто и доходчиво написано. Спасибо автору!

  24. Дархан:

    Отличная статья! Самое главное код рабочий. Взял, поставил себе - осталось только доработать под свои нужды. К слову ajax вообще не знаю

  25. Констан:

    Кто тут критиковал насчет невалидности. Если так боитесь поисковиков, заключайте этот фрагмент в ноиндекс и прочие подобные вещи.

  26. haZe:

    Хмм, интересное решение, понравилось. А окончание загрузки определять по readyState?
    А не найдется такое же решение, нос мультизагрузкой, т.е. выделять кучу файлов и чтоб они загружались.
    Да и вопрос на счет уязвимости, я слышал сайт как то через фреймы легко сломать, но еще не сталкивался с подробной информацией.
    Поклон за отличную идею!

  27. admin:

    А окончание загрузки определять по readyState?
    В заметке написано, что к AJAX (асинхронная отправка данных на сервер) этот способ не имеет отношения. Так что какой readyState?
    выделять кучу файлов и чтоб они загружались
    А кто мешает сделать форму с несколькими полями загрузки файлов?

  28. Вопрос:

    Вопрос, загрузка то происходит, но как потом получить информацию о загруженном файле и переместить его куда надо?

  29. admin:

    как потом получить информацию о загруженном файле
    Из массива $_FILES: Загрузка файлов на сервер
    и переместить его куда надо
    move_uploaded_file()

  30. Devoter:

    Статья больше напоминает собственные записки из разряда “чтобы не забыть”, но уж никак не полезный интернет-ресурс. Зря потратил время на прочтение статьи (любой человек, немного понимающий в логике и web-программировании сам до этого дойдет), особенно, если учесть тот факт, что средствами ajax МОЖНО загрузить файл на сервер (и при том - не один!).

Оставьте свой отзыв