Хороший скрипт – это инструмент, который сэкономит вам массу времени и нервных клеток при разработке веб-приложения. По своему опыту создания сайтов знаю, что значительную часть времени приходиться тратить именно на поиск готовых «велосипедов» и решений, которые упрощают нашу работу. Сегодня мы затронем одну из важнейших тем – загрузка файлов или картинок на сервер при помощи ajax. И, хотя есть множество статьей и скриптов, я расскажу своё видение данной проблемы и её решения у себя в проекте.
Речь пойдёт об одном из готовых инструментов под названием AJAX File-Uploader версии 2.0, которое если и не использовать при возникновении необходимости загрузки файлов, то уж точно добавить к себе в закладки. Почему именно этот плагин? Я, наверно, перебрал уже немало вариантов и сохранил столько же ссылок у себя на ПК, где рассматриваются различные решения, и каждое из них имеет право на существование, но этот плагин после подробного его исследования мне показался максимально удобным и универсальным.
Что же он может делать?
С одной стороны смешной вопрос, естественно загружать файлы! Но давайте рассмотрим отличительные его черты и возможности:
- Плагин поддерживает Multiple функционал;
- Есть собственный прогрессбар из коробки;
- Поддерживает возможность Drag-and-drop;
- Процесс загрузки отменяемый;
- Довольно важная особенность – не использует Flash;
- Работает во всех современных браузерах, а, что касается IE, то сам лично проверял – поддерживает начиная с 7 версии.
Как видите, этого уже не мало, но ещё хочется добавить, что при его настройке очень удобно отлавливать различные события, а также обрабатывать данные на сервере, собственно, об этом далее.
Если у вас медиаресурс или вы просто захотели добавить возможность пользователям загружать файлы, то, как это в основном происходит: пользователь кликает по кнопке загрузить, выбрав картинку, и ожидает какого-то результата -> в это время его картинка попадает на сервер, где что-то с ней делаем уже мы. Да это очень-очень упрощённая схема загрузки, но, тут важно, что весь процесс можно разделить на два этапа: визуальная часть и серверная часть. В этой статье мы и рассмотрим эту цепочку на примере, который вы можете изучить в демо, прикреплённом к этой статье.
В самом начале работы с плагином нам необходимо подключить следующие файлы:
<link href="main.css" type="text/css" rel="stylesheet" />
<link href="fileuploader.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="fileuploader.js" type="text/javascript"></script>
Данное решение написано на JavaScript и не использует сторонних библиотек. jQuery я подключил для собственного удобства, а в файле стилей main – мы будем добавлять свои CSS правела, чтобы не затронуть стили которые нам предоставляют разработчики.
Теперь мы добавим HTML разметку:
<div id="file-uploader">
<noscript>
<p>
Пожалуйста, включите JavaScript, чтобы использовать загрузчик файлов.
</p>
</noscript>
</div>
Если вы не ориентируетесь на людей, которые не используют JavaScript, то элемент noscript и его содержимое можно не писать.
Добавим ещё блок, в котором мы будем выводить состояния загрузки файлов:
<div id="file-logs">
</div>
Закончив с подготовительными делами, переходим непосредственно к работе со скриптом, который поместим в стандартную jQuery обёртку.
$(function(){
/*
Весь JavaScript
добавляем сюда
*/
});
Теперь добавим функцию, при помощи которой мы будем формировать шаблон для вывода состояний при загрузке файла и событий, которые могут возникнуть:
function templateLogsHtml(element, title, type, args){
var templateHtml = "",
argsHtml = "";
for(var key in args){
if(typeof(args[key]) == "object"){
for(var k in args[key]){
argsHtml += "<b>" + k + " - " + args[key][k] + "</b>";
};
}else{
argsHtml += key + " - " + args[key] + "<br />";
};
};
templateHtml = "<p class='" + type + "'>";
templateHtml += "<span>" + title + ":</span>";
templateHtml += argsHtml;
templateHtml += "</p>";
element.prepend(templateHtml);
return false;
};
Функция templateLogsHtml() представляет собой простейший шаблонизатор для вывода наших логов для различных событий. Рассмотрим принимаемые ею параметры:
- element – блок в который мы будем выводить логи;
- title – заголовок события;
- type – тип события;
- args – аргументы события.
Теперь я добавлю сам код уже настроенного загрузчика, а ниже под ним опишу все его составляющие:
var fileLogs = $("#file-logs");
var uploader = new qq.FileUploader({
element: $("#file-uploader")[0],
action: "server.php",
allowedExtensions: ["jpg", "jpeg", "png", "gif", "ico"],
sizeLimit: 51200,
multiple: 5,
inputName: "qqFile",
onSubmit: function(fileId, fileName){
templateLogsHtml(fileLogs, "Нажатие на кнопку", "onSubmit", {
ID: fileId,
NAME: fileName
});
},
onCancel: function(fileId, fileName){
templateLogsHtml(fileLogs, "Отмена загрузки", "onCancel", {
ID: fileId,
NAME: fileName
});
},
onComplete: function(fileId, fileName, responseJSON){
templateLogsHtml(fileLogs, "Ответ сервера", "onComplete", {
ID: fileId,
NAME: fileName,
RESPONSE: responseJSON
});
},
messages: {
typeError: "Допустимые форматы для загрузки: {extensions}",
sizeError: "Файл {file} слишком большой, Вы превысили допустимый размер {sizeLimit}"
},
showMessage: function(message){
templateLogsHtml(fileLogs, "Вывод сообщения об ошибке", "showMessage", {
MESSAGE: message
});
},
dragText: "Переместите файл сюда чтобы загрузить",
uploadButtonText: "Загрузить файл",
cancelButtonText: "Отмена",
failUploadText: "Файл не загрузился"
});
Из этого кода уже можно понять, что настройка довольно проста и понятна, теперь давайте рассмотрим все методы и свойства загрузчика, использованные для примера из демо:
- element – узел DOM дерева в нашем случае <div id="file-uploader">;
- action – путь до скрипта обработчика;
- allowedExtensions – массив с форматами файлов допускаемых для загрузки;
- sizeLimit – минимальный размер файла в байтах;
- multiple – число файлов загружаемых за раз, если нужен только один указываем в значении !1;
- inputName – значение параметра name у input через который будет осуществляться загрузка;
- onSubmit – callback функция которая вызывается при нажатии на кнопку загрузки;
- onCancel – callback функция вызываемая при отмене;
- onComplete – callback функция при вызываемая при удачном завершении загрузки файла;
- messages – сообщения ошибок;
- showMessage – callback функция при помощи которой мы можем вывести ошибки на экран;
- dragText – текст на области Drag-and-drop;
- uploadButtonText – текст на кнопке загрузки;
- cancelButtonText – текст кнопки отмены;
- failUploadText – текст при не удачной загрузке.
Но это далеко не все параметры, которые можно использовать - их гораздо больше! С другими вы можете ознакомиться, воспользовавшись поиском по самой библиотеке.
Стили, которые визуализируют пример из демо находиться в файле main.css, я не стал их добавлять в статью, так как они не имеют большого значения, а в архиве есть.
Теперь давайте перейдём к серверу и разберемся, как мы будем обрабатывать данные. Если перейти на сайт разработчиков, ссылку вы найдёте в низу страницы и скачать там архив с плагином, то в нём будет готовый PHP сервер. Из себя он представляет файл с тремя классами. После его рассмотрения, я его немного подправил под себя. Конечно изменения не существенные, но есть, поэтому я буду отталкиваться от моего варианта.
Чтобы добиться большей кроссбраузерности разработчики AJAX File-Uploader версии 2.0 воспользовались двумя способами загрузки, а именно через ajax и через форму. Зависимости от поддерживаемого метода браузером подключается тот или иной способ. Такая же ситуация и на сервере, у нас - три класса UploaderFileByXhr, UploaderFileByForm, Uploader. Первый и второй отвечают каждый за свой способ загрузки, а третий используется для подключения одного из вариантов, а также обработки принимаемых параметров и проверки загрузки файла.
class UploaderFileByXhr{
private $name;
public function __construct($name){
$this->name = $name;
return false;
}
public function save($path){
$input = fopen("php://input", "r");
$temp = tmpfile();
$real_size = stream_copy_to_stream($input, $temp);
fclose($input);
if($real_size != $this->get_size()){
return false;
}
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
return true;
}
public function get_size(){
if(isset($_SERVER["CONTENT_LENGTH"])){
return (int)$_SERVER["CONTENT_LENGTH"];
}
return false;
}
public function get_name(){
return $_GET[$this->name];
}
}
class UploaderFileByForm{
private $name;
public function __construct($name){
$this->name = $name;
return false;
}
public function save($path){
if(!move_uploaded_file($_FILES[$this->name]["tmp_name"], $path)){
return false;
}
return true;
}
public function get_size(){
return $_FILES[$this->name]["size"];
}
public function get_name(){
return $_FILES[$this->name]["name"];
}
}
class Uploader{
private $file;
private $formats;
private $max_size;
public function __construct($params = array()){
if(isset($params["file"]) && $params["file"]){
if(isset($_GET[$params["file"]])){
$this->file = new UploaderFileByXhr(
$params["file"]
);
}elseif(isset($_FILES[$params["file"]])){
$this->file = new UploaderFileByForm(
$params["file"]
);
}else{
$this->file = false;
}
}
if(isset($params["formats"]) && $params["formats"]){
$this->formats = strtolower($params["formats"]);
}
if(isset($params["max_size"]) && $params["max_size"]){
$this->max_size = (int)$params["max_size"];
}
return false;
}
public function upload_file($path, $file_name){
if(!is_writable($path)){
return array(
"error" => "Ошибка сервера, невозможно загрузить в папку"
);
}
if(!$this->file){
return array(
"error" => "Файл не был загружен"
);
}
$size = $this->file->get_size();
if($size == 0){
return array(
"error" => "Файл пуст"
);
}
if($size > $this->max_size){
return array(
"error" => "Файл слишком большой"
);
}
$path_info = pathinfo($this->file->get_name());
$ext = @strtolower($path_info["extension"]);
$valid_ext = explode("|", $this->formats);
if($this->formats && !in_array($ext, $valid_ext)){
$these = implode(", ", $valid_ext);
return array(
"error" => "Допустимые форматы для загрузки: ".$these
);
}
if($this->file->save($path.$file_name.".".$ext)){
return array(
"success" => true
);
}
return array(
"error" => "Файл не был загружен или отменён"
);
}
}
Для загрузки файла на сервер необходимо создать экземпляр объекта Uploader с переданными в него параметрами:
- file – название атрибута name, который был рассмотрен выше;
- formats – форматы файлов допускаемые для загрузки;
- max_size – максимальный размер файла.
$obj = new Uploader(array(
"file" => "qqFile",
"formats" => "jpg|jpeg|png|gif|ico",
"max_size" => 51200
));
А теперь останется воспользоваться методом upload_file():
echo(json_encode(
$obj->upload_file("uploads/", "newName")
));
Данный метод принимает два параметра:
- Папка, в которую нужно сохранить файл;
- Имя добавленного файла.
Напоследок: после того, как вы скачаете архив, строки, отвечающие за загрузку, будут закомментированы, для использования их стоит раскомментировать.
И так, подведём итог разбора плагина AJAX File-Uploader версии 2.0: это довольно хорошие и удобное решение для загрузки файлов, которое универсально и кроссбраузерно, в большинстве случаев оно будет лучшим выбором для загрузки файлов и картинок, но недостаток – обладает значительным размером.
Надеюсь, эта статья помогла вам разобраться с принципом загрузки файлов при помощи ajax на сервер.