AJAX. Обмен данными между клиентом и сервером, закачка на сервер файлов без перезагрузки страницы при помощи библиотеки jQuery.
Ромка — Ср, 09/05/2007 - 15:44
Задача
Разработать веб-страницу, позволяющую обмениваться данными и закачивать файлы на сервер без перезагрузки страницы.
Средства
Frontend (клиентская часть) – библиотека jQuery версии 1.1.4 и плагин к ней ajaxUpload;
Backend (серверная часть) – Apache (любой версии), PHP 5.2.3, MySQL. В PHP 5.2.0 появились встроенные средства для работы с данными в формате JSON, которые используются в этом примере, если на вашем хостинге установлена более старая версия PHP, то эти функции придется написать самостоятельно.
Решение
Блок-схема работы скрипта изображена на рисунке (большая картинка по клику). Пунктиром обозначен момент обмена данными между клиентом и сервером.
Теперь та же логика, только словами:
1. Сначала пользователь заполняет форму и жмет кнопку "Отправить", затем клиентский скрипт (frontend) передает серверному (backend) текст из формы (передается только текст, без файла, логика простая – зачем передавать файл, если уже в тексте может быть ошибка?).
2. Серверный скрипт проверяет текст на наличие ошибок и возвращает результат клиентскому скрипту (в этом случае в формате html).
3. Клиентский скрипт обрабатывает ответ от сервера, если в ответе передана ошибка, то выводится соответствующее сообщение и скрипт завершает работу, если ошибок нет, то клиентский скрипт отдает серверному файл, выбранный пользователем.
4. Серверный скрипт проверяет корректность файла (размер, тип и т.п.) и отдает ответ клиентскому скрипту (на этот раз в формате JSON).
5. Клиентский скрипт обрабатывает полученный ответ и выводит на экран соответствующий результат.
Исходники и комментарии
В конце статьи будут даны ссылки на полные исходные коды всех файлов. Ниже приведены комментарии к самым важным участкам кода.
Форма запроса (html, файл add.php)
В тэгах head подключаем библиотеку jQuery, плагин ajaxUpload и файл с нашим frontend'ом:
<?php<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="ajaxupload.js"></script>
<script type="text/javascript" src="scriptik.js"></script>
?>
Далее рисуем форму, для отправки текста и файла:
<?php
<form enctype="multipart/form-data" method=post name=jklm>
<input name=m1 value=""><br>
<input name=m2 value=""><br>
<input type="file" name="img">
<input type=button value="Добавить сообщение" onclick="javascript:ajax(this.form.m1.value, this.form.m2.value, this.form);" class=subm>
</form>
?>
M1 и m2 – это два текстовых поля, данные из которых будут записаны в БД на сервере, img – поле для выбора закачиваемого файла, в данном примере рассмотен вариант с закачкой картинки.
В инпуте типа button, на событие onclick установлена функция, отправляющая данные на сервер. Этой функции передается содержимое текстовых полей и название формы. Сама функция будет описана ниже.
Далее рисуем два слоя, в одном будет выводиться сообщение вида "Подождите идет загрузка", во втором – все остальные сообщения, в том числе и сообщение об успешном завершении работы скрипта:
<?php
<div id=loading><img src=loading.gif></div>
<div class="m"></div>
?>
Картинка loading.gif должна лежать в той же папке, что и текущий файл (или пропишите в тэге img соответствующий путь).
Все, больше ничего важного в форме запроса нет, остальной код в этом файле – украшательства, не влияющие на работу примера.
Frontend (Javascript, файл scriptik.js)
Здесь описаны только функции из файла scriptik.js, отвечающие за передачу/прием данных от сервера, остальные функции носят чисто украшательский характер и их описание выходит за рамки этой статьи.
Передаем backend файлу insert.php данные из текстовых полей:
- $.ajax(
- {
- type: "POST",
- url: "insert.php",
- data: "x1=" + m1 + "&x2=" + m2,
Обрабатываем ответ сервера. Логика работы серверного скрипта такая: если в переданном клиентом тексте были найдены ошибки, то, в зависимости от ошибки, будет возвращено какое-либо отрицательное число. Если в переданном тексте ошибок нет, то в ответе от сервера придет положительное число – id записи в БД, с которым сохранился этот текст:
- success: function(data){
- if(data <= -1)show_error_message(data);
- else {
- if(formname.img.value != ""){
- $.ajaxUpload({
- url:'imageupload.php?k=' + data,
- secureuri:false,
- uploadform: formname,
- dataType: 'json',
То есть, если мы получили отрицательный результат, то выводим сообщение об ошибке, если получили положительный результат, то приступаем к закачке файла на сервер. Imageupload.php – backend, отвечающий за закачку файла и его соответствие некоторым требованиям. Скрипту imageupload.php методом GET передается id, под которым на сервере был сохранен переданный текст, чтобы с тем же id сохранить и файл.
Опять обрабатываем ответ сервера, теперь уже ответ приходит в формате JSON, по этому к переменным, пришедшим в ответе можно получить доступ используя объект вида result.var1, result.var2 и т.д.
- success: function (img_upload, status){
- $("div#loading").hide();
- if(img_upload.result == "IMG_UPLOAD_OK")$("div.m").html("Сообщение успешно добавлено");
- else $("div.m").html("Сообщение успешно добавлено, но картинку закачать не удалось.");
- $('div.m').animate({height: 'show'}, 500);
- },
- error: function (data, status, e){
- $("div.m").html("Ошибка добавления данных. " + e);
img_upload – это объект, в котором сохраняется результат. Сервер передает клиенту две переменные: img_upload.result – информация о том закачалась картинка или нет, img_upload.name – имя, под которым картинка сохранена на сервере.
Backend (PHP, файлы insert.php и imageupload.php)
Здесь также описаны только функции для взаимодействия сервера с клиентом, описания вспомогательных функций опущены.
insert.php – проверка на корректность, запись в БД переданного клиентом текста и передача ответа клиенту.
Для безопасности проверяем пришел запрос через XMLHttpRequest или нет:
<?php
if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
?>
Пишем данные в базу и отдаем ответ клиенту:
<?php
if(mysql_query("INSERT INTO messages (m1, m2, date) VALUES ('" . htmlspecialchars($_POST["x1"]) . "', '" . htmlspecialchars($_POST["x2"]) . "', NOW())")){
$last_id = mysql_insert_id();
echo $last_id;
}
else echo "-2";// Ошибка подключения к БД
?>
imageupload.php – проверка на корректность закачанного файла, копирование файла в нужную папку и передача ответа клиенту.
<?php
// Проверяем переданный id записи на то, чтобы в нем содержались только цифры
$id = $_GET['k'];
$id = preg_replace("/\D/", "", $id);
if(intval($id)!= $id){
$arr = array ('result'=>"IMG_UPLOAD_ERROR_3:" . intval($id) . ":" . $id);
exit (json_encode($arr));
}
$id = intval($id);
// Проверяем, что закачана картинка, если закачана не картинка, то возвращаем ошибку
if(is_uploaded_file($_FILES['img']['tmp_name'])){
if($_FILES['img']['type'] != "image/bmp" && $_FILES['img']['type'] != "image/jpeg" && $_FILES['img']['type'] != "image/gif" && $_FILES['img']['type'] != "image/png" && $_FILES['img']['type'] != "image/pjpeg"){
$arr = array ('result'=>"IMG_UPLOAD_ERROR_WRONG_FILE_TYPE");
exit (json_encode($arr));
}
// Проверяем размер файла
if($_FILES['img']['size'] >= 100000){
$arr = array ('result'=>"IMG_UPLOAD_ERROR_IMAGE_TO_BIG");
exit (json_encode($arr));
}
$name = $_FILES['img']['name'];
$dot = strrpos($name, ".");
$dot = strlen($name) - $dot;
$dot = -$dot;
$ext = substr($name, $dot);
// Перемещаем закачанный файл из временной папки и возвращаем результат frontend'у
if(move_uploaded_file($_FILES['img']['tmp_name'], $_SERVER['DOCUMENT_ROOT'] . "/uploadimages/" . $id . $ext)){
$arr = array ('result'=>'IMG_UPLOAD_OK','name'=> $id . $ext);
echo json_encode($arr);
} else {
$arr = array ('result'=>"IMG_UPLOAD_ERROR_1: " . $_FILES['img']['tmp_name']);
exit (json_encode($arr));
}
} else {
$arr = array ('result'=>"IMG_UPLOAD_ERROR_2");
exit (json_encode($arr));
}
?>
Вот собственно и все. Готов ответить на любые вопросы.
Все исходники в аттаче, не забудьте в файле db_connect.php прописать свои настройки для доступа к БД, а также, перед началом работы создать базу данных из sql-файла, который лежит в архиве.
Добрый
Anonymous → Втр, 10/30/2007 - 15:12Добрый день.
Интересная статья, а главное нужная в данный момент.
Возникает только один вопрос - а где ссылка на аттач ?
Очень
Anonymous → Чт, 11/01/2007 - 05:55Очень интересно!!!
Особенно удивляет тот момент, что на многих форумах пишут, что мол через аякс нельзя загрузить файлы :)
Но все-таки хотелось посмотреть живые файлы, которые вы прикрипили, но их не видно :(
Буду очень вам благодарен за них.
Денис
открываем http://fiv
Anonymous → Втр, 12/25/2007 - 22:58открываем
http://fivethreeo.dynalias.org/media/ajaxupload.js
if(window.ActiveXObject) {
var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
.....
}
else {
var io = document.createElement('iframe');
.....
Чуда не произошло :-)
Эээ... это
Ромка → Втр, 12/25/2007 - 23:58Эээ... это вообще к чему?
Исправил глюк,
Ромка → Пт, 11/02/2007 - 14:41Исправил глюк, сейчас файл должен быть доступен для скачивания (ссылка внизу первого поста).
Есть вопрос.
Anonymous → Чт, 11/15/2007 - 17:18Есть вопрос. Дело втом что после сабмита выбрасывает иногда на разные страницы в чем модет ьыть дело???
Хмм... А как
Ромка → Чт, 11/15/2007 - 20:05Хмм... А как сабмитишь? Форму приведи тут... И, желательно, клиентский и серверный скрипты, в том виде, в котором ты их используешь... Просто фронтэнд после сабмита вообще никуда тебя перебрасывать не должен, все делается в пределах одной страницы, без перезагрузки...
Отличная
Anonymous → Пт, 11/23/2007 - 13:26Отличная статья!
Ошибка кстати в
Anonymous → Пт, 11/23/2007 - 16:13Ошибка кстати в библятеке этой ajaxUpload. Там
s.uploadform.target" = frameId надо заменить на
s.uploadform.attr("target", frameId); (строка128 или где-то в этом районе). В противном случае страничка перегружается
Хммм... Честно
Ромка → Сб, 11/24/2007 - 13:00Хммм... Честно говоря, у меня все корректно работает и без правки ядра jQuery и ее плагинов...
Факт! Надо фиксить
Anonymous → Втр, 09/30/2008 - 20:47Тоже нашёл такой баг! Самое интересное проявляется не всегда!
После указанного выше фикса всё встало на свои места!
Спасибо!
Браузер какой?
Ромка → Ср, 10/01/2008 - 00:08Браузер какой?
Не помогло!
Anonymous → Втр, 09/30/2008 - 20:58Кстате не помогло! Пришлось делать перед сабмитом
document.frm.action = '';
document.frm.target = '';
document.frm.submit();
:( Изгаляется гад на target как хочет!
а у меня
Гость (не проверено) → Ср, 01/30/2008 - 22:58а у меня почемуто в БД не пишет
По такому
Ромка → Чт, 01/31/2008 - 00:26По такому малоинформативному сообщениию сложно выводы делать. Какой код используете? Какие ошибки выдаются? Есть ли доступ к логам?
З.Ы. А вообще целью этого примера было показать как с использованием AJAX закачивать файлы на сервер, а не как писать данные в БД, по базам данных в интернете немало документации.
а у меня все
Гость (не проверено) → Пт, 02/08/2008 - 17:34а у меня все делает кромк как закачивает саму картинку и в базу пишет и картинка загрузки выводит, но файл не пишет. В чем может быть проблема?
Какую при этом
Ромка → Пт, 02/08/2008 - 20:03Какую при этом выдает ошибку сам скрипт? Он должен писать что-то вроде "Сообщение успешно добавлено, однако картинку закачать не удалось, по этому используется картинка по умолчанию. Сообщение об ошибке: IMG_UPLOAD_ERROR_IMAGE_TO_BIG". Сообщения, разумеется, могут быть разными. Какие ошибки пишутся в лог веб-сервера? Без этой информации определить в чем проблема не получится.
Перепробовал
Sr (не проверено) → Пт, 02/08/2008 - 23:25Перепробовал всё. БД обновляется, но значок загрузки крутиться не перестаёт, а картинка не аплоадится
Попробуй
Ромка → Пт, 02/08/2008 - 23:47Попробуй поставить браузер Firefox и расширение к нему Firebug. Потом запусти в браузере страницу со скриптом и в правом нижнем углу браузера щелкни по зеленой галочке (или, в случае если скрипт выполняется с ошибками, вместо зеленой галочки может находиться красный крестик). В появившемся окне на вкладке "Console" будут показаны все запросы переданные браузером серверу и все ответы сервера. Попробуй выяснить на каком этапе случается сбой...
Кстати, только сейчас в голову идея пришла! А включена ли поддержка JSON в PHP? Очень похоже что нет. Проверить это можно в phpinfo().
Good Article, Author -
Anonymous → Пнд, 03/17/2008 - 14:26Good Article, Author - Thanks!
Спасибо за
Smak → Вс, 03/23/2008 - 15:27Спасибо за отличную статью!
Очень хорошо всё показано на блок схеме.
Какой софт использовал для её создания?
:)) Вообще-то это
Ромка → Ср, 03/26/2008 - 00:55:)) Вообще-то это Ворд с его автофигурами :)) Тогда под рукой ничего более подходящего не оказалось :))
Драстя. Недавно
Roman [Yollopukki] (не проверено) → Чт, 06/12/2008 - 17:53Драстя. Недавно начал изучать принципы ajax, а с jQuery всего второй день. Как оно работает впринципе понять не сложно, а вот как закинуть на сервер файло (а то и несколько файло) при этом не выкидывая пользователя со страницы, до настоящего момента оставалось загадкой. Но все оказалось как незя проще. Спасибо за пример. Great Respect и Уважуха.
Супер!
Гость (не проверено) → Ср, 08/06/2008 - 03:23Супер!
И все-таки он грузит через
Anonymous → Ср, 09/10/2008 - 18:08И все-таки он грузит через iframe:
ajaxfileupload.js:
<?php
createUploadIframe: function(id, uri)
{
//create frame
var frameId = 'jUploadFrame' + id;
if(window.ActiveXObject) {
var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
if(typeof uri== 'boolean'){
io.src = 'javascript:false';
}
else if(typeof uri== 'string'){
io.src = uri;
}
}
....
?>
Думаю не стоит объяснять, что значит конструкция
<?phpvar io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');?>
Респектую тебе Ромка статья
Anonymous (не проверено) → Пнд, 11/17/2008 - 05:49Респектую тебе Ромка статья зачот.
Спасибо за статью! А архив
Anonymous (не проверено) → Втр, 03/03/2009 - 15:43Спасибо за статью! А архив недоступен для скачивания? Запрашиваемая страница не найдена
интересно а как ты не через
wanders.32 (не проверено) → Ср, 04/01/2009 - 15:49интересно а как ты не через iframe загрузишь файл? или с перезагрузкой всей страницы, или через фрейм. разве есть другие какие-то способы?
Аттач
Solven (не проверено) → Чт, 04/16/2009 - 20:44Мега респект! =)
Как раз то что нужно для галереи ))
Только аттач не работает, перезалейте плиз...
Отзыв
Валера М. (не проверено) → Пт, 04/24/2009 - 15:27Статья нормуль, сам скрипт не запускал, могу только пару замечаний:
1) "onclick="javascript:ajax(t"
Это не есть гуд, т.к. считается old-style и невалидно вроде как, используйте id и addEvent function
2) Неплохо бы сделать progress-bar, чтобы было видно сколько еще осталось качать
Возникла необходимость в
Сергей (не проверено) → Пнд, 04/27/2009 - 13:59Возникла необходимость в похожем функционале, но не знаю как это использовать с друпал 6.10
мне нужно:
1. вывести форму в которую человек вводит число
2. число отправляется на сервер и проверяется по массиву (скажем из 10 чисел)
3. в случае совпадения выводится число из другого массива
что получается:
в ноду вставляю код:
Мы приглашаем Вас принять участие в нашем опросе
Введите число из 12 цифр:
//Здесь, я так понимаю, должны вызываться фунцкии описанные в файле scriptik.js отвечающие за прием передачу от сервера.
//в файл insert.php передаются данные (должна проводиться проверка на коректность и запись в БД) и обрабатывается ответ от сервера
который выводит приглашение и форму для ввода.
ввожу число, крутится картинка loading.gif и ничего не происходит.
наверно где-то на уровне insert.php (или раньше в scriptik.js) происходит ошибка, но как отследить ее не знаю.
Подскажите пожалуйста. сам я новичек, но очень хочется разобраться :)
код забыл добавить
Сергей (не проверено) → Пнд, 04/27/2009 - 14:01<?php
<head>
<script type="text/javascript" src="/misc/jquery.js"></script>
<script type="text/javascript" src="/myscripts/scriptik.js"></script>
</head>
<body>
<center><table width=600 cellpadding=10>
<form enctype="multipart/form-data" method=post name=jklm>
<tr><td>Мы приглашаем Вас принять участие в нашем опросе</td></tr>
<td width=50%><br>Введите число из 12 цифр:<br><input name=m1 value="0"><br><br></td>
<tr><td><div align=right><input type=button value="Отправить" onclick="javascript:ajax(this.form.m1.value);" class=subm></div>
//Здесь, я так понимаю, должны вызываться фунцкии описанные в файле scriptik.js отвечающие за прием передачу от сервера.
//в файл insert.php передаются данные (должна проводиться проверка на коректность и запись в БД) и обрабатывается ответ от сервера
</td></tr>
</form>
</table></center>
<div id=loading style='position: absolute; left: 50%; width:50%;'><img src=/myscripts/loading.gif></div>
<br><br>
<div class="m" style='position: absolute; left: 50%; width:50%; margin-left:-25%;'></div>
</body>
</html>
?>
Плагин на эту тему был бы очень полезен
masster (не проверено) → Втр, 04/28/2009 - 08:17Плагин на эту тему был бы очень полезен, именно в той логике, что в статье и конечно с прогресс баром. А за статью - спасибо!
Открывается новое окно
zver911 (не проверено) → Сб, 07/18/2009 - 20:20Здравствуйте, очень хорошая статья и я использовал ее у себя в проекте.
Возникла проблема, Input Files находится в форме, которая содержит много другой информации, которая обрабатывается PHP с последующим Redirect после добавления в БД. Все работает, только результат выводится в новое окно и, похоже, обрабатывается повторно (выдает мою ошибку, что объект уже существует) но в БД записывается 1 раз.
Чтото не работает
vladex (не проверено) → Чт, 10/15/2009 - 03:54Установил все как надо.
Только сменил название таблицы messages на messagi
Во первых не понимается кодировка, но это пол беды.
Если нажать на кнопку для загрузки фотки, на выбирая фото, то сообщения добавляются, и внизу формы выезжает картинка с надписями сверху и снизу.
Но если я выберу картинку для загрузки и нажму ЗАГРУЗИТЬ то картинка закачки крутиться, и все.
Ничего не грузит на сервер.
В настройках апача json включен.
Вопрос пока без ответа
Alexx (не проверено) → Пт, 12/25/2009 - 15:16Здравствуйте , Ромка !
Буду краток. Нужно проверить размер файла НЕПРЕМЕННО ДО загрузки. Как ?
Буду очень признателен...
Автор, в чем ты рисовал
Anonymous (не проверено) → Втр, 12/29/2009 - 09:37Автор, в чем ты рисовал блок-схему?
read or die
Anonymous (не проверено) → Пнд, 04/12/2010 - 06:56MS Office 2007 Visio
Есть, конечно, на разных
rangerover → Втр, 05/18/2010 - 11:46Есть, конечно, на разных должностях в здесь, но я не нашел этой должности в связи с проектами, как cfa practice test... Если кто-то информация о нем, скажи! Ну, все обновления, связанные с этой позиции? Если да, то скажите мне! На самом деле я пришел сюда во время путешествия по сети для получения данных, относящихся к cfp practice test проектов и нашли эту должность в другой ... Есть ли кто, располагающие информацией о cgfns practice test , chmm practice test? Если да, то что делать, скажите! Мне кажется, другой тип пост ... Тот, кто не знает об этом раньше можно получить полезную информацию из этого поста ... Ну, я хочу сказать, что, как вы пытались объяснить некоторые из должностей, здесь я думаю, разные ...
!
Максим Кредшев → Втр, 06/01/2010 - 18:47Добрый день!
Смотрим как работает модуль с уведомление отправкой!
Фрейм... :-)
Коня (не проверено) → Пт, 06/18/2010 - 13:56Я тоже понадеялся на чудо. Но увы...
Сразу же бросился в глаза доктайп 1.0 Strict.
Т.к. он НИ ПОД КАКИМ СОУСОМ не воспримет фрейм, подумал, что реализовано без фреймов. А на деле просто ошибочный доктайп. Не говоря уже о куче мелочей, т.к.
пишется с закрытым тегом () впрочем как и любые другие тэги в этом доктайпе. Про формы и не говорю... . Измени доктайп, а то люди путаются... Загрузить без фрейма по-любому никак всё равно. А сам скрипт хороший, задолбался искать такой. Молодец. Здорово сократил мне время работы.
очень красивая схема. именно.
человек (не проверено) → Ср, 08/25/2010 - 17:07очень красивая схема. именно.
НЕ работает скрипт в Chrome
wonderer22 (не проверено) → Вс, 10/10/2010 - 09:20В Firefox все нормально, в chrome не работает
при загрузке страницы сразу крутится loading.gif
кнопка добавить не нажимается :(
менял тип кнопки на submit, кнопка оживает
значит chrome не понимает конструкцию вида
?
serialize
Sanyame (не проверено) → Втр, 03/22/2011 - 23:18А как можно передать данные m1 и m2 с помощью функции serialize? чтобы сделать пример универсальным