Jquery отправка файла на сервер. Загрузка файлов на сервер с помощью ajax

Добрый день!

Долгое время на просторах интернета я искал информацию о реализации AJAX загрузки файлов для CodeIgniter. Разные разработчики предлагали разные технологии и примеры реализации. Я перепробовал их все, но ни одна из них не была достаточно проста и функциональна одновременно. Лишь недавно я открыл для себя jQuery File Uploader . «Он ничем не отличается от остальных» - скажите вы, но это не так. Его главное отличие - это простота и хорошая документация с примерами. В документации разобраны все callback"и, описаны все options. Внедрение в любую систему не занимает много времени.

Сегодня я покажу как можно очень просто организовать multipart загрузку файлов на сервер + drug&drop в CodeIgniter.

jQuery File Uploader + CodeIgniter Из коробки CodeIgniter предлагает нам использовать библиотеку $this->load->library("upload"); , которая позволяет контролировать передаваемые файлы, ограничивая загрузку по типу, размеру, ширине и высоте изображения. Использовать ее легко и удобно, но следует отметить небольшое ограничение налагаемое на INPUT данной библиотекой. Поле INPUT должно обязательно иметь параметр name=«userfile» . Соглашаемся с этим фактом и переходим в Controller к функции которая будет вызывать библиотеку Upload и, собственно, сохранять наши файлы на диск.

Пример реализации 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) поможет найти много интересных вещей, таких как: количество отправленных файлов, ошибки и многое другое.

Вот собственно и все, надеюсь на полезность материала.

В сети существует множество библиотек реализующих загрузку файлов на сервер. Так что у разработчика есть широкий выбор каким инструментом можно воспользоваться. Но порой они довольно сложные для новичком. Изначально для статьи было решено написать простую библиотеку, которую будет легко понять. Но по счастью в сети была найдена довольно простая реализация загрузки фалов, которой было и решено воспользоваться. Называется она 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" } ) ; } } ) ; } ) ;

Все! Клиенсткая часть готова. Немного подробнее:

  • DmUploader за нас подпишется на события drag для дом элемента #dropzone, а также на событие change для элемента input file.
  • При наступлении одного из событий, будет прочитан локальный файл с помощью file api.
  • У файла будет проверен размер и тип. В нашем случае, только картинки.
  • Удовлетворяющий всем проверкам файл будет загружен на сервер.
  • В процессе работы будут вызываться соответствующие коллбэки (callbacks). Например, onUploadSuccess вызовется после успешной загрузки файла. Вторым аргументом в функцию поступит ответ от сервера в формате json. Коллбэков у библиотеки больше, чем представлено в примере. С помощью них можно реализовать более сложные задачи.
  • Сервер

    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 ) ; }

    На основе демо вы можете углубить свои знания в этой области. В исходных кодах даны ссылки для дальнейшего изучения. Эксперементируйте!

    Последнее обновление: 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); } });

    Мы пробежались по нескольким основным методам для получения данных и их дальнейшей передаче AJAX-запросом. Теперь пришло время поговорить о том, как же можно загружать файлы с помощью AJAX . Еще до недавнего времени, способов загружать файлы без перезагрузки самой страницы, было не так уж и много (скрытый iframe, Flash). Они и сейчас используются по причине того, что еще остаются пользователи со старыми версиями браузеров, которых не коснулся прогресс. Но оглядываться назад не будем, посему шагаем в ногу со временем.

    Рассмотрим, на мой взгляд, один из самых удобных способов для работы с файлами (и не только) - объект FormData . Пусть будет такая простенькая форма, для загрузки аватара пользователя:

    HTML (файл index.html )

    Ф.И.О:
    Аватар:

    Перейдем к JS-части. С полем "Ф.И.О" сложностей не будет и его используем только для наглядности того, что вместе с файлом, мы можем отправлять любые другие данные.

    jQuery (файл script.js )

    $(function(){ $("#my_form").on("submit", function(e){ e.preventDefault(); var $that = $(this), formData = new FormData($that.get(0)); // создаем новый экземпляр объекта и передаем ему нашу форму (*) $.ajax({ url: $that.attr("action"), type: $that.attr("method"), contentType: false, // важно - убираем форматирование данных по умолчанию processData: false, // важно - убираем преобразование строк по умолчанию data: formData, dataType: "json", success: function(json){ if(json){ $that.replaceWith(json); } } }); }); });

    (*)Обратите внимание на то, что передаем форму не объектом jQuery, а DOM-элемент

    PHP-обработчик (файл handler.php )



    
    Top