AJAX. Обмен данными между клиентом и сервером, закачка на сервер файлов без перезагрузки страницы при помощи библиотеки jQuery.

Аватар пользователя Ромка Ромка 6 сентября 2007 в 13:55

Статья к Друпалу имеет очень косвенное отношение (пример использует js-библиотеку jQuery, поставляемую вместе с Друпалом), но, думаю, может показаться интересной посетителям сайта.

Оригинал, немного подругому отформатированный лежит на моем сайте.

Задача

Разработать веб-страницу, позволяющую обмениваться данными и закачивать файлы на сервер без перезагрузки страницы.

Средства

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'ом:

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="ajaxupload.js"></script>
<script type="text/javascript" src="scriptik.js"></script>

Далее рисуем форму, для отправки текста и файла:

<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 установлена функция, отправляющая данные на сервер. Этой функции передается содержимое текстовых полей и название формы. Сама функция будет описана ниже.

Далее рисуем два слоя, в одном будет выводиться сообщение вида "Подождите идет загрузка", во втором – все остальные сообщения, в том числе и сообщение об успешном завершении работы скрипта:

<div id=loading><img src=loading.gif></div>
<div class="m"></div>

Картинка loading.gif должна лежать в той же папке, что и текущий файл (или пропишите в тэге img соответствующий путь).

Все, больше ничего важного в форме запроса нет, остальной код в этом файле – украшательства, не влияющие на работу примера.

Frontend (Javascript, файл scriptik.js)

Здесь описаны только функции из файла scriptik.js, отвечающие за передачу/прием данных от сервера, остальные функции носят чисто украшательский характер и их описание выходит за рамки этой статьи.

Передаем frontend файлу 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));      
}
?>

Вот собственно и все. Готов ответить на любые вопросы, выслушать конструктивную критику и исправить все выявленные неточности и недочеты Lol

ВложениеРазмер
Иконка пакета ajax-jquery-upload-sources.zip23.88 КБ

Комментарии

Аватар пользователя Ромка Ромка 6 сентября 2007 в 14:48

Ага, Камбоджа классная страна: красивая, гостеприимная, с доброжелательными жителями... И в ней не так много туристов как в Тае, хотя климат там такой же... Правда и развлечений для туристов, кроме небольшого количества экскурсий, там практически нет...

Аватар пользователя vadbars@drupal.org vadbars@drupal.org 6 сентября 2007 в 16:37

Сайт красивый. Покупали уже тему для Drupal или просто дизайн?

p.s. Текст на главной начинается с ошибок и опечаток - "добро пожаловть", "на инересные темы". И много повторений слова "мне", "мои" и т.п. Надо бы поправить. Smile
Фото красивые, но большие. Имхо, миниатюры надо или под кат. А то левая колонка здоровая и на 15'' картинка уезжает вправо. Появляется горизонтальная прокрутка.

Аватар пользователя Ромка Ромка 6 сентября 2007 в 16:48

Спасибо Lol

Покупал дизайн (кратинки и html-шаблон), а темплейтмонстре, макет нарезал сам. Был один неприятный сюрприз после покупки. На скриншоте-превьюшке не было видно, что шаблон заточен под фиксированную ширину колонки, причем под очень маленькую ширину. Пришлось попотеть в фотошопе, чтобы сделать шаблон резиновым.

За ошибки спасибо, сейчас исправлю... Мне это "мне-мне-мне" тоже в глаза бросилось, сейчас попробую перефразировать...

Аватар пользователя VladSavitsky VladSavitsky 11 сентября 2007 в 21:01

[http://dklab.ru/lib/JsHttpRequest/manual.html JsHttpRequest] - отличная библиотека, где закача файла уже вшита и не является проблемой.

Лично пробовал - всё работает как часы.

И главное идея оригинальная! Мне как РНР-программисту очень по душе такой подход.

Аватар пользователя Ромка Ромка 11 сентября 2007 в 22:32

Библиотек для работы с AJAX море, важно выбрать наиболее удобную и универсальную из них. В jQuery закачка файлов также не является проблемой, но кроме того эта библиотека является мощным инструментом для создания визуальных эффектов и удобным обработчиком событий. Плюс у jQuery нет привязки к PHP-backend'у, на сервере может работать хоть программа написанная на вижуалбейсике.

Я, например, почти на всех своих сайтах использую jQuery для реализации тех или иных эффектов на стороне юзера, по этому мне нет резона устанавливать еще какую-то дополнительную библиотеку для работы с AJAX'ом. Людям использующим Друпал тоже нет смысла использовать альтернативные библиотеки, так как jQuery уже встроена в Друпал.

Аватар пользователя Ромка Ромка 2 ноября 2007 в 14:14

Ммм... Так собственно привязки к Друпалу тут нет. Да и в БД данные пишутся чисто для примера, можно просто исключить весь код связанный с записью данных в БД из скрипта и все. Если хотите, то опишите подробно свою задачу, а я адаптирую под нее пример.

Аватар пользователя genzo_vs genzo_vs 27 июля 2008 в 17:56

А как можно сделать upload файлов используя только библиотеку Jquery без каких либо дополнительных плагинов? Хотелось бы взглянуть на код

Аватар пользователя Diezz Diezz 14 мая 2009 в 13:02

в ajaxupload иногда возникает синтаксическая ошибка в if(type=="json")
eval("data = "+data);
Ошибка добавления данных. [Error: name: SyntaxError message: Statement on line 29: Syntax error Backtrace: Line 29 of inline#8 script in http://xxxxx/ eval("data = "+data);if(type=="html") Line 11 of inline#8 script in http://xxxxx/ if(xml||isTimeout=="timeout"){requestDone=true;var status;try{status=isTimeout!="timeout"?"success":"error";if(status!="error"){var data=jQuery.uploadHttpData(xml,s.dataType);if(s.success) ... ]

Аватар пользователя Dark_kz Dark_kz 5 июля 2009 в 23:16

У меня на локальной машине так и не заработало. Картинка loading стоит и никуда не уходит, записи в базе появляются нормально. Попробую-ка на сервере..
upd: на сервере выдало такую ошибку: IMG_UPLOAD_ERROR_1: /var/www/clients/client56/web182/tmp/phpEcX5E0
объясните пожалуйста чтобы это могло означать?