Добрый день!
Долгое время на просторах интернета я искал информацию о реализации AJAX загрузки файлов для CodeIgniter. Разные разработчики предлагали разные технологии и примеры реализации. Я перепробовал их все, но ни одна из них не была достаточно проста и функциональна одновременно. Лишь недавно я открыл для себя jQuery File Uploader . «Он ничем не отличается от остальных» - скажите вы, но это не так. Его главное отличие - это простота и хорошая документация с примерами. В документации разобраны все callback"и, описаны все options. Внедрение в любую систему не занимает много времени.
Сегодня я покажу как можно очень просто организовать multipart загрузку файлов на сервер + drug&drop в CodeIgniter.
Пример реализации PHP функции:
public function upload(){
$config["upload_path"] = "/application/uploads/";
$config["allowed_types"] = "jpg|jpeg|png|gif|flv|mp4|wmv|doc|docx|xsl|xslx|ppt|pptx|zip|rar|tar";
$config["max_size"] = 2048;
$config["max_width"] = 800;
$config["max_height"] = 600;
$config["encrypt_name"] = TRUE;
$this->load->library("upload", $config);
if ($this->upload->do_upload() == false) {
$error = array("error" => $this->upload->display_errors());
echo json_encode($error);
}else{
$data = $this->upload->data();
echo json_encode($data);
}
}
Внимание! Для того, что бы у Вас работали все allowed_types необходимо дописать недостающие MIME-Types в конфигурационный файл /application/config/mimes.php
У нас готова функция для сохранения файла на сервер. Переходим к клиентской части. Нам понадобится скачать с Github jQuery File Upload . Плагин предоставляет большие возможности, но все их использовать мы не будет, воспользуемся лишь загрузкой нескольких файлов, drug&drop и progressall.
Подключаем на страницу загрузки необходимые JS:
- jquery.fileupload.js
- jquery.fileupload-video.js
- jquery.fileupload-process.js
- jquery.iframe-transport.js
- upload.js //В комплекте не идет - напишем сами
И CSS файл:
- css/jquery.fileupload.css
Добавляем наш INPUT на страницу:
Добавить файл
Осталось совсем не много - написать upload.js, который будет прослушивать событие изменения поля INPUT и вызывать загрузку выбранного файла. «А где же обещанный Drug&Drop?» - спросите Вы. Drug&Drop уже работает благодаря jQuery File Upload. Вместо вызова стандартного диалога выбора файла вы можете перетащить сразу несколько файлов на страницу и они в порядке очереди загрузятся на сервер.
И на последок Upload.js
$(document).ready(function(){
$("#fileupload").fileupload({
dataType: "json",
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$(".progress .bar").css("width", progress + "%"); },
done: function (e, data) {
if(data.result.error != undefined){
$("#error").html(data.result.error); // выводим на страницу сообщение об ошибке если оно есть
$("#error").fadeIn("slow");
}else{
$("#error").hide(); //на случай если сообщение об ошибке уже отображалось
$("#files").append("");
$("#success").fadeIn("slow");
}
}
}
});
});
Data - это наш ответ от сервера, но он не является массивом с информацией о загруженном файле. Вся информация в формате JSON хранится в Data.Result. Кстати говоря console.log(data) поможет найти много интересных вещей, таких как: количество отправленных файлов, ошибки и многое другое.
Вот собственно и все, надеюсь на полезность материала.
В наше время веб-сайты становятся все более интерактивными. Это касается не только специализированных сервисов, но и обычных интернет магазинов, блогов и небольших сайтов. Основной особенностью является асинхронный JavaScript и XML сокращенно AJAX . Эта технология позволяет браузеру в фоновом режиме общаться с веб-сервером и при обновлении данных, веб-страница не перезагружается полностью. Другими словами, мы можем делать запросы и получать ответы от сервера не перезагружая страницу в браузере.
Поскольку наиболее популярным языком для разработки веб-приложений является PHP , то сегодня мы будем использовать связку AJAX и PHP . Пример будет хорош для понимания основных принципов работы с AJAX и PHP .
На самом деле особых сложностей быть не должно, алгоритм действий:
- Выбрать картинку
- Нажать кнопку “Отправить”
- Перехватить вызов формы с помощью JavaScript (jQuery)
- Передать содержимое в специальный php скрипт-обработчик
- Вернуть результат выполнения
- Обработать результат при помощи JavaScript (jQuery)
- Вывести пользователю информацию о загрузке
Немного отклонюсь от темы и объясню что такое jQuery . jQuery — это специальная JavaScript библиотека, которая помогает упростить разработку веб приложений в несколько раз, также данная библиотека предоставляет API для работы с AJAX . Простыми словами, мы напишем меньше кода, чем если бы это делали на чистом JS.
Ajax позволяет не перезагружая веб-страницу, обмениваться данными с веб-сервером и обновлять ее содержимое.
Я склоняюсь к тому, что если есть инструмент, который позволяет вам ускорить разработку без последствий, то почему бы его не использовать? Но чистый JS тоже не помешало бы знать(хоть и лично мой уровень владения JS равен уровню копипаста примеров со stackoverflow 🙂).
Мы разберем одну из проблем, которую мне однажды пришлось решать, а именно — загрузка изображения на сайт с предварительным просмотром. Если вы меняли аватарку в вконтакте, вы понимаете о чем я пишу.
Нам понадобится 3 простых файла, это:
- Страница с формой
- php обработчик
- файл js
Обычная html страница с формой. Обратите внимание на enctype="multipart/form-data" , это нужно для передачи файлов, параметр указывает на способ кодирования данных. Если передаете файлы, значение всегда должно быть multipart/form-data .
handler.php // Проверяем установлен ли массив файлов и массив с переданными данными if(isset($_FILES) && isset($_FILES["image"])) { //Переданный массив сохраняем в переменной $image = $_FILES["image"]; // Проверяем размер файла и если он превышает заданный размер // завершаем выполнение скрипта и выводим ошибку if ($image["size"] > 200000) { die("error"); } // Достаем формат изображения $imageFormat = explode(".", $image["name"]); $imageFormat = $imageFormat; // Генерируем новое имя для изображения. Можно сохранить и со старым // но это не рекомендуется делать $imageFullName = "./images/" . hash("crc32",time()) . "." . $imageFormat; // Сохраняем тип изображения в переменную $imageType = $image["type"]; // Сверяем доступные форматы изображений, если изображение соответствует, // копируем изображение в папку images if ($imageType == "image/jpeg" || $imageType == "image/png") { if (move_uploaded_file($image["tmp_name"],$imageFullName)) { echo "success"; } else { echo "error"; } } }Это очень упрощенный обработчик. Имя картинки я сгенерировал использовав функцию hash . Хорошей практикой считается изменять имена файлов при загрузке их на сервер.
ajaxupload.js $(document).ready(function () { function readImage (input) { if (input.files && input.files) { var reader = new FileReader(); reader.onload = function (e) { $("#preview").attr("src", e.target.result); } reader.readAsDataURL(input.files); } } function printMessage(destination, msg) { $(destination).removeClass(); if (msg == "success") { $(destination).addClass("alert alert-success").text("Файл успешно загружен."); } if (msg == "error") { $(destination).addClass("alert alert-danger").text("Произошла ошибка при загрузке файла."); } } $("#image").change(function(){ readImage(this); }); $("#upload-image").on("submit",(function(e) { e.preventDefault(); var formData = new FormData(this); $.ajax({ type:"POST", // Тип запроса url: "handler.php", // Скрипт обработчика data: formData, // Данные которые мы передаем cache:false, // В запросах POST отключено по умолчанию, но перестрахуемся contentType: false, // Тип кодирования данных мы задали в форме, это отключим processData: false, // Отключаем, так как передаем файл success:function(data){ printMessage("#result", data); }, error:function(data){ console.log(data); } }); })); });В этом скрипте происходит самое интересное. При помощи функции readImage() мы будем считывать файл с поля формы и передавать его в блок для предварительного просмотра. Создается объект FileReader . Он позволяет веб-приложению считывать содержимое файла на компьютере пользователя. Событие.onload сработает когда содержимое будет считано, при помощи этого события мы выведем изображение в блок предварительного просмотра.
И напоследок, метод.readAsDataURL() запускает процесс чтения файла, по завершению чтения будет выполнено событие.onload и картинка появится у вас на экране.
Функция printMessage создана для вывода информации об успешной или провалившейся попытке загрузки файла. Подробно не рассматриваем, ничего особенного не представляет.
Перехват формы и её обработка. При клике на кнопку «Отправить» событие будет перехвачено скриптом и при помощи функции.preventDefault() форма не отправит данные в index.html . .preventDefault() служит для отмены вызова каких-либо событий.
Объект FormData нужен нам для создания POST запроса к нашему скрипту, это намного проще чем вписывать каждый элемент формы в строку. Создали объект, заполнили данными, отдали в наш ajax .
Ну и собственно сам запрос AJAX . Поскольку мы используем библиотеку jQuery , составить и выполнить такой запрос не вызовет у вас никаких проблем.
Собственно, на этом и закончим. Изображение загружается, страница не перезагружается, все довольны. Если у вас возникают вопросы или предложения, пишите комментарии.
Хорошего дня и успехов 🙂
Последнее обновление: 1.11.2015
В статье были рассмотрены общие моменты оптравки файлов на сервер в обычном запросе POST. Но если мы используем Ajax-запросы, то загрузка файлов будет иметь свои особенности.
Сначала определим метод в контроллере MVC:
Public JsonResult Upload() { foreach (string file in Request.Files) { var upload = Request.Files; if (upload != null) { // получаем имя файла string fileName = System.IO.Path.GetFileName(upload.FileName); // сохраняем файл в папку Files в проекте upload.SaveAs(Server.MapPath("~/Files/" + fileName)); } } return Json("файл загружен"); }
Здесь предполагается, что у нас в проекте определена папка Files для хранения загруженных файлов. Для получения файлов используется коллекция Request.Files
После сохранения файла пользователю отдается результат в виде строки.
Код представления тогда будет выглядеть следующим образом:
@{
ViewBag.Title = "Home Page";
}
Загрузить
@section scripts{
$("#submit").on("click", function (e) {
e.preventDefault();
var files = document.getElementById("uploadFile").files;
if (files.length > 0) {
if (window.FormData !== undefined) {
var data = new FormData();
for (var x = 0; x < files.length; x++) {
data.append("file" + x, files[x]);
}
$.ajax({
type: "POST",
url: "@Url.Action("Upload", "Home")",
contentType: false,
processData: false,
data: data,
success: function (result) {
alert(result);
},
error: function (xhr, status, p3) {
alert(xhr.responseText);
}
});
} else {
alert("Браузер не поддерживает загрузку файлов HTML5!");
}
}
});
}
Нам не нужна стандартная форма, все делается через ajax. Сначала получаем все выбранные файлы:
Var files = document.getElementById("uploadFile").files
Затем формируем объект FormData , в который добавляем все выбранные файлы:
Var data = new FormData(); for (var x = 0; x < files.length; x++) { data.append("file" + x, files[x]); }
И отсылаем их на сервер.
Асинхронная загрузка файлов в WebAPIВ Web API контроллер будет выглядеть следующим образом:
Public class ValuesController: ApiController { public async Task Post() { if (!Request.Content.IsMimeMultipartContent()) { return BadRequest(); } var provider = new MultipartMemoryStreamProvider(); // путь к папке на сервере string root = System.Web.HttpContext.Current.Server.MapPath("~/Files/"); await Request.Content.ReadAsMultipartAsync(provider); foreach (var file in provider.Contents) { var filename = file.Headers.ContentDisposition.FileName.Trim("\""); byte fileArray = await file.ReadAsByteArrayAsync(); using (System.IO.FileStream fs = new System.IO.FileStream(root + filename, System.IO.FileMode.Create)) { await fs.WriteAsync(fileArray, 0, fileArray.Length); } } return Ok("файлы загружены"); } }
Здесь используется асинхронная обработка запроса. Вначале метод IsMultipartContent() проверяет, содержит ли запрос корректные данные. Если нет, то возвращаем статусный код 401.
Для асинхронного чтения с потока создается провайдер:
Var provider = new MultipartMemoryStreamProvider(); Request.Content.ReadAsMultipartAsync(provider);
После считывания потока свойство provider.Contents будет содержать все считанные значения, в том числе и файлы. И в цикле проходим по всем файлам и читаем их в массив байтов:
Byte fileArray = await file.ReadAsByteArrayAsync();
Затем с помощью объекта FileStream считанный массив сохраняется на диске в папке проекта.
Представление будет тем же, что и для MVC, за исключением url запроса:
$.ajax({ type: "POST", url: "api/values/post", contentType: false, processData: false, data: data, success: function (result) { alert(result); }, error: function (xhr, status, p3) { alert(status); } });
В сети существует множество библиотек реализующих загрузку файлов на сервер. Так что у разработчика есть широкий выбор каким инструментом можно воспользоваться. Но порой они довольно сложные для новичком. Изначально для статьи было решено написать простую библиотеку, которую будет легко понять. Но по счастью в сети была найдена довольно простая реализация загрузки фалов, которой было и решено воспользоваться. Называется она dmuploader и является плагином для jQuery.
С помощью dmuploader мы создадим загрузчик, похожий по дизайну на тот, что реализован в wordpress. С Drag And Drop’ом и кликами! Преступим!
Структура проекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #dropzone { border : 4px dashed #bbb ; border-radius : 5px ; color : #444 ; padding : 25px ; text-align : center ; } #dropzone .title { font-size : 20px ; } #dropzone input { -moz-border-bottom-colors: none ; -moz-border-left-colors: none ; -moz-border-right-colors: none ; -moz-border-top-colors: none ; border-color : transparent ; border-image: none ; cursor : pointer ; direction : ltr ; margin : 0 ; opacity: 0 ; position : absolute ; right : 0 ; top : 0 ; transform: translate(-300px , 0px ) scale(4 ) ; } #dropzone .browser span { background : #f7f7f7 ; border : 1px solid #ccc ; color : #555 ; cursor : pointer ; font-size : 16px ; height : 46px ; line-height : 44px ; padding : 0 36px ; } #dropzone .browser span:hover { border : 1px solid #999 ; } |
Javascript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // index.php $(document) .ready (function () { $("#dropzone" ) .dmUploader ({ url: "upload.php" , dataType: "json" , maxFileSize: 256 * 1014 , allowedTypes: "image/*" , onBeforeUpload: function (id) { $) .show () ; } , onUploadSuccess: function (id, response) { $("div#output" ) .html ("" ) ; } , onUploadError: function (id, message) { $.jGrowl ("Файл: " + id + " не загрузился: " + message, { theme: "error" } ) ; } , onFileTypeError: function (file) { $.jGrowl (, { theme: "error" } ) ; } , onFileSizeError: function (file) { $.jGrowl ("Файл слишком большой!!" , { theme: "error" } ) ; } , onFallbackMode: function (message) { $.jGrowl ("Ваш браузер не поддерживается 8(" , { theme: "error" } ) ; } } ) ; } ) ; |
Все! Клиенсткая часть готова. Немного подробнее:
Сервер
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | // upload.php require_once __DIR__ . "/protected/bootstrap.php" ; if (! IS_POST() || ! $_FILES ) { stopAndResponseMessage("error" , "Только POST, FILES" } $files = convertFileInformation($_FILES ) ; if (! isset ($files [ "file" ] ) ) { stopAndResponseMessage("error" , "Файл не загружался" ) ; } $file = $files [ "file" ] ; if ($file [ "error" ] !== UPLOAD_ERR_OK) { stopAndResponseMessage( "error" , uploadCodeToMessage($file [ "error" ] ) ) ; } $mimeType = guessMimeType($file [ "tmp_name" ] ) ; if (! $mimeType ) { stopAndResponseMessage("error" , "Тип файла не распознается!" ) ; } $validMimeType = [ "image/png" , "image/jpeg" ] ; if (! in_array ($mimeType , $validMimeType ) ) { stopAndResponseMessage( "error" , "Загружать можно только png и jpeg!" ) ; } $size = filesize ($file [ "tmp_name" ] ) ; if ($size > 256 * 1024 ) { stopAndResponseMessage("error" , "Файл слишком большой!!" ) ; } $uploadDir = __DIR__ . "/files" ; if (! is_writable ($uploadDir ) ) { stopAndResponseMessage( "error" , "Папка для файлов не доступна для записи." ) ; } $filename = time () . "-" . mt_rand (0000 , 9999 ) . "." . guessFileExtension($mimeType ) ; if (! move_uploaded_file ( $file [ "tmp_name" ] , $uploadDir . "/" . $filename ) ) { stopAndResponseMessage("error" , "Файл не был перемецен!" ) ; } sendResponse("upload" , [ "url" => "files/" . $filename ] ) ; |
Стоит заострить особое внимание на функции guessMimeType. Она с помощью расширения FileInfo определяет MIME тип файла. Как видно из кода, тип и размер файла, присланные от браузера не определяются при проверке. Их может подделать злоумышленник.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //protected/inc/func.php function guessMimeType($path , $magicFile = null ) { if (! is_file ($path ) ) { return null ; } if (! is_readable ($path ) ) { return null ; } if (! $finfo = new \finfo(FILEINFO_MIME_TYPE, $magicFile ) ) { return ; } return $finfo -> file ($path ) ; } |
На основе демо вы можете углубить свои знания в этой области. В исходных кодах даны ссылки для дальнейшего изучения. Эксперементируйте!
Как загружать любые файлы, например, картинки на сервер с помощью AJAX и jQuery? Делается это довольно просто! И ниже мы все обстоятельно разберем.
В те «древние» времена, когда еще не было jQuery, а может он был, но браузеры были не так наворочены, загрузка файла на сайт с помощью AJAX была делом муторным: через всякие костыли вроде iframe. Я те время не застал, да и кому это теперь интересно. А интересно теперь другое - что сохранение файлов на сайт делается очень просто. Даже не обладающий опытом и пониманием, того как работает AJAX, вебмастер, сможет быстро разобраться что-куда. А эта статья ему в помощь. Если подкрепить эти возможности функциями WordPress , то безопасная обработка и загрузка файлов на сервер становится совсем плевым и даже интересным делом (пример с WordPress смотрите в конце статьи).
Однако, как бы все просто не было, нужно заметить, что минимальный опыт работы с файлами и базовые знания в Javascript, jQuery и PHP все же необходимы! Минимум, нужно представлять как загружаются файлы на сервер, как в общих чертах работает AJAX и хоть немного надо уметь читать и понимать код.
Описанный ниже метод довольно стабилен, и по сути опирается на Javascript объект new FormData() , базовая поддержка которого есть во всех браузерах.
Для более понятного восприятия материала, он разделен на шаги. На этом все, полетели...
AJAX Загрузка файлов: общий примерНачинается все с наличия на сайте input поля типа file . Нет необходимости, чтобы это поле было частью формы (тега ).
Таким образом, у нас есть HTML код с file полем и кнопкой «Загрузить файлы».
Загрузить файлы
Шаг 1. Данные из поля fileПервым шагом, нужно получить данные загружаемых файлов.
При клике на file-поле, появляется окно выбора файлов, после выбора, данные о них сохраняются в input поле, а нам нужно их от туда «забрать». Для этого повесим на событие change JS функцию, которая будет сохранять имеющиеся данные file-поля в JS переменную files:
Var files; // переменная. будет содержать данные файлов // заполняем переменную данными, при изменении значения поля file $("input").on("change", function(){ files = this.files; });
Шаг 2. Создаем AJAX запрос (по клику)Данные файлов у нас есть, теперь их нужно отправить через AJAX. Вешаем это событие на клик по кнопке «Загрузить файлы».
В момент клика создаем новый объект new formData() и добавляем в него данные из переменной files . С помощью formData() мы добьемся того, что отправляемые данные будут выглядеть, как если бы мы просто сабмитили форму в браузере.
Чтобы такой запрос состоялся, в jQuery нужно указать дополнительные AJAX параметры, поэтому привычная функция $.post() не подходит и мы используем более гибкий аналог: $.ajax() .
Два важных дополнительных параметра нужно установить в false:
ProcessData
Отключает обработку передаваемых данных. По умолчанию, например, для GET запросов jQuery собирает данные в строку запроса и добавляет эту строку в конец URL. Для POST данных делает другие преобразования. Нам любые изменения исходных данных будут мешать, поэтому отключаем эту опцию...
contentType
Отключает установку заголовка типа запроса. Дефолтная установка jQuery равна "application/x-www-form-urlencoded . Такой заголовок не предусматривает отправку файлов. Если установить этот параметр в "multipart/form-data" , PHP все равно не сможет распознать передаваемые данные и выведет предупреждение «Missing boundary in multipart/form-data»... В общем, проще всего отключить эту опция, тогда все работает!
// обработка и отправка AJAX запроса при клике на кнопку upload_files
$(".upload_files").on("click", function(event){
event.stopPropagation(); // остановка всех текущих JS событий
event.preventDefault(); // остановка дефолтного события для текущего элемента - клик для тега
// ничего не делаем если files пустой
if(typeof files == "undefined") return;
// создадим объект данных формы
var data = new FormData();
// заполняем объект данных файлами в подходящем для отправки формате
$.each(files, function(key, value){
data.append(key, value);
});
// добавим переменную для идентификации запроса
data.append("my_file_upload", 1);
// AJAX запрос
$.ajax({
url: "./submit.php",
type: "POST", // важно!
data: data,
cache: false,
dataType: "json",
// отключаем обработку передаваемых данных, пусть передаются как есть
processData: false,
// отключаем установку заголовка типа запроса. Так jQuery скажет серверу что это строковой запрос
contentType: false,
// функция успешного ответа сервера
success: function(respond, status, jqXHR){
// ОК - файлы загружены
if(typeof respond.error === "undefined"){
// выведем пути загруженных файлов в блок ".ajax-reply"
var files_path = respond.files;
var html = "";
$.each(files_path, function(key, val){
html += val +"
";
})
$(".ajax-reply").html(html);
}
// ошибка
else {
console.log("ОШИБКА: " + respond.error);
}
},
// функция ошибки ответа сервера
error: function(jqXHR, status, errorThrown){
console.log("ОШИБКА AJAX запроса: " + status, jqXHR);
}
});
});
Теперь последний шаг: нужно обработать отправленный запрос.
Чтобы было наглядно обработаем запрос без дополнительных проверок для файлов, т.е. просто сохраним полученные файлы в нужную папку. Хотя, для безопасности, отправляемые файлы обязательно нужно проверять, хотя бы расширение (тип) файла...
Создадим файл submit.php с таким кодом (предполагается что submit.php лежит в той же папке, где и файл, с которого отправляется AJAX запрос):
jQuery(document).ready(function($){
// ссылка на файл AJAX обработчик
var ajaxurl = "";
var nonce = "";
var files; // переменная. будет содержать данные файлов
// заполняем переменную данными, при изменении значения поля file
$("input").on("change", function(){
files = this.files;
});
// обработка и отправка AJAX запроса при клике на кнопку upload_files
$(".upload_files").on("click", function(event){
event.stopPropagation(); // остановка всех текущих JS событий
event.preventDefault(); // остановка дефолтного события для текущего элемента - клик для тега
// ничего не делаем если files пустой
if(typeof files == "undefined") return;
// создадим данные файлов в подходящем для отправки формате
var data = new FormData();
$.each(files, function(key, value){
data.append(key, value);
});
// добавим переменную идентификатор запроса
data.append("action", "ajax_fileload");
data.append("nonce", nonce);
data.append("post_id", $("body").attr("class").match(/postid-(+)/));
var $reply = $(".ajax-reply");
// AJAX запрос
$reply.text("Загружаю...");
$.ajax({
url: ajaxurl,
type: "POST",
data: data,
cache: false,
dataType: "json",
// отключаем обработку передаваемых данных, пусть передаются как есть
processData: false,
// отключаем установку заголовка типа запроса. Так jQuery скажет серверу что это строковой запрос
contentType: false,
// функция успешного ответа сервера
success: function(respond, status, jqXHR){
// ОК
if(respond.success){
$.each(respond.data, function(key, val){
$reply.append("");
});
}
// error
else {
$reply.text("ОШИБКА: " + respond.error);
}
},
// функция ошибки ответа сервера
error: function(jqXHR, status, errorThrown){
$reply.text("ОШИБКА AJAX запроса: " + status);
}
});
});
})
Антивирус Bitdefender: эффективный защитник Без вопросов
Значение слова неудачный
Обзор Samsung Galaxy A7 (2017): не боится воды и экономии Стоит ли покупать samsung a7
Делаем бэкап прошивки на андроиде
Как настроить файл подкачки?