Интерактивная загрузка файлов на HTML5 и jQuery
Сегодня мы сделаем небольшое веб-приложение, которое позволит пользователям загружать фотографии со своего компьютера, просто перетаскивая их в окно браузера. Это возможно благодаря новым возможностям HTML5.
У фото в нашем приложении будут небольшие иконки и индикатор загрузки, которые будут создаваться на стороне клиента. Пока изображения просто хранятся в папке на сервере, но вы можете расширить функционал.
HTML5 загрузка файлов
Для начала разберемся что такое загрузка с помощью HTML5. На самом деле это комбинация трех новых технологий: File Reader API, Drag & Drop API и AJAX. Рассмотрим процесс загрузки:
- Пользователь перетаскивает один или несколько файлов в окно браузера. Браузер, поддерживающий Drag & Drop API генерирует событие, которое наряду с другой информацией содержит список файлов.
- Используя File Reader API, мы считываем файлы и запоминаем их.
- Используя новый метод sendAsBinary объекта XMLHttpRequest, мы отправляем файлы на сервер.
Все эти шаги сделает за нас jQuery плагин Filedrop. Плагин позволяет нам задавать максимальный размер файлов и определять функции обратного вызова, что очень полезно для встраивания в ваше веб приложение.
Минус данного метода в том, что он работает не во всех браузерах. Хотя последние версии поддерживают данные функции, сделать стандартную форму загрузки не будет лишним. В нашем примере мы не будем уделять этому внимания.
HTML
Начнем с разметки нашего приложения. Это простая html5 страница, мы подключаем стили, script.js, Filedrop plugin и jQuery.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>HTML5 File Drag and Drop Upload with jQuery and PHP | Tutorialzine Demo</title> <!-- стили --> <link rel="stylesheet" href="assets/css/styles.css" /> <!--[if lt IE 9]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <header> <h1>HTML5 File Upload with jQuery and PHP</h1> </header> <div id="dropbox"> <span>Drop images here to upload. <br /><i>(they will only be visible to you)</i></span> </div> <!--jQuery--> <script src="http://code.jquery.com/jquery-1.6.3.min.js"></script> <!-- библиотека для загрузки файлов --> <script src="assets/js/jquery.filedrop.js"></script> <!-- главный скрипт --> <script src="assets/js/script.js"></script> </body> </html>
Единственный элемент div #dropbox будет содержать список файлов, которые на него перетащили.
Когда пользователь перетащит файл, jQuery добавит на страницу следующий html код, который будет отображать уменьшенное изображение:
<div class="preview done"> <span class="imageHolder"> <img src="" /> <span class="uploaded"></span> </span> <div class="progressHolder"> <div class="progress"></div> </div> </div>
Данный фрагмент содержит иконку изображения и линию загрузки файла. Элемент, содержащий миниатюру может иметь класс .done, он означает, span.uploaded должен стать видимым(по-умолчанию скрыт). Этот элемент содержит зеленую галку, которая появляется после полной загрузки файла.
jQuery
Загрузкой файла будет заниматься плагин Filedrop нам надо только вызвать его и передать несколько колбеков для нужд нашего приложения. Также мы напишем небольшой PHP скрипт, который будет управлять загрузкой на стороне сервера.
Сначала напишем вспомогательную функцию, которая принимает объект, содержащий информацию о файле (специальный объект, созданный браузером и содержащий некоторые параметры, например, имя, путь, размер файла). Эта функция будет создавать разметку для миниатюры.
var template = '<div class="preview">'+ '<span class="imageHolder">'+ '<img />'+ '<span class="uploaded"></span>'+ '</span>'+ '<div class="progressHolder">'+ '<div class="progress"></div>'+ '</div>'+ '</div>'; function createImage(file){ var preview = $(template), image = $('img', preview); var reader = new FileReader(); image.width = 100; image.height = 100; reader.onload = function(e){ // e.target.result содержит путь к изображению image.attr('src',e.target.result); }; // считываем файл, при завершении будет вызвана функция onload (выше) reader.readAsDataURL(file); message.hide(); preview.appendTo(dropbox); $.data(file,preview); }
Теперь перейдем к вызову плагина.
$(function(){ var dropbox = $('#dropbox'), message = $('.message', dropbox); dropbox.filedrop({ // имя поля в $_FILES: paramname:'pic', maxfiles: 5, maxfilesize: 2, // в Мб url: 'post_file.php', uploadFinished:function(i,file,response){ $.data(file).addClass('done'); // ответ сервера - это JSON объект, котоорый вернет post_file.php }, error: function(err, file) { switch(err) { case 'BrowserNotSupported': showMessage('Your browser does not support HTML5 file uploads!'); break; case 'TooManyFiles': alert('Too many files! Please select 5 at most!'); break; case 'FileTooLarge': alert(file.name+' is too large! Please upload files up to 2mb.'); break; default: break; } }, // вызывается перед загрузкой beforeEach: function(file){ if(!file.type.match(/^image\//)){ alert('Only images are allowed!'); return false; } }, uploadStarted:function(i, file, len){ createImage(file); }, progressUpdated: function(i, file, progress) { $.data(file).find('.progress').width(progress); } }); var template = '...'; function createImage(file){ // ... выше ... } function showMessage(msg){ message.html(msg); } });
Все корректные файлы, будут переданы в post_file.php, код которого вы увидите ниже.
PHP
Серверная часть кода ничем не отличается от обычной загрузки. Поэтому вы можете менять её на свое усмотрение.
// если вы не хотите загружать файлы присвойте $demo_mode значение true; $demo_mode = false; $upload_dir = 'uploads/'; $allowed_ext = array('jpg','jpeg','png','gif'); if(strtolower($_SERVER['REQUEST_METHOD']) != 'post'){ exit_status('Error! Wrong HTTP method!'); } if(array_key_exists('pic',$_FILES) && $_FILES['pic']['error'] == 0 ){ $pic = $_FILES['pic']; if(!in_array(get_extension($pic['name']),$allowed_ext)){ exit_status('Only '.implode(',',$allowed_ext).' files are allowed!'); } if($demo_mode){ // загрузка не происходит $line = implode(' ', array( date('r'), $_SERVER['REMOTE_ADDR'], $pic['size'], $pic['name'])); file_put_contents('log.txt', $line.PHP_EOL, FILE_APPEND); exit_status('Uploads are ignored in demo mode.'); } // перемещаем файл из временной папки в папку хранения на сервере if(move_uploaded_file($pic['tmp_name'], $upload_dir.$pic['name'])){ exit_status('File was uploaded successfuly!'); } } exit_status('Something went wrong with your upload!'); // вспомогательные функции function exit_status($str){ echo json_encode(array('status'=>$str)); exit; } function get_extension($file_name){ $ext = explode('.', $file_name); $ext = array_pop($ext); return strtolower($ext); }
Код проверяет HTTP метод запроса и формат переданного файла. Переменная $demo_mode нужна только для демонстрации приложения, мы не вызываем move_uploaded_file, поэтому файл автоматически удаляется после обработки запроса.
CSS
Рассмотрим только часть кода CSS. Весь код доступен в исходниках.
/*------------------------- Dropbox Element --------------------------*/ #dropbox{ background:url('../img/background_tile_3.jpg'); border-radius:3px; position: relative; margin:80px auto 90px; min-height: 290px; overflow: hidden; padding-bottom: 40px; width: 990px; box-shadow:0 0 4px rgba(0,0,0,0.3) inset,0 -3px 2px rgba(0,0,0,0.1); } #dropbox .message{ font-size: 11px; text-align: center; padding-top:160px; display: block; } #dropbox .message i{ color:#ccc; font-size:10px; } #dropbox:before{ border-radius:3px 3px 0 0; } /*------------------------- Image Previews --------------------------*/ #dropbox .preview{ width:245px; height: 215px; float:left; margin: 55px 0 0 60px; position: relative; text-align: center; } #dropbox .preview img{ max-width: 240px; max-height:180px; border:3px solid #fff; display: block; box-shadow:0 0 2px #000; } #dropbox .imageHolder{ display: inline-block; position:relative; } #dropbox .uploaded{ position: absolute; top:0; left:0; height:100%; width:100%; background: url('../img/done.png') no-repeat center center rgba(255,255,255,0.5); display: none; } #dropbox .preview.done .uploaded{ display: block; } /*------------------------- Progress Bars --------------------------*/ #dropbox .progressHolder{ position: absolute; background-color:#252f38; height:12px; width:100%; left:0; bottom: 0; box-shadow:0 0 2px #000; } #dropbox .progress{ background-color:#2586d0; position: absolute; height:100%; left:0; width:0; box-shadow: 0 0 1px rgba(255, 255, 255, 0.4) inset; -moz-transition:0.25s; -webkit-transition:0.25s; -o-transition:0.25s; transition:0.25s; } #dropbox .preview.done .progress{ width:100% !important; }
Меняя ширину .progress мы получим интерактивный индикатор загрузки.
На этом всё! 🙂
Здравствуйте! Спасибо большое за урок! Хотел бы спросить, есть ли возможность приделать удаления фотографий, после того как их перетащили и они видны в виде миниатюр?
Вот пример кода:
// перемещаем файл из временной папки в папку хранения на сервере
if(move_uploaded_file($pic[‘tmp_name’], $upload_dir.$pic[‘name’])){
exit_status(‘File was uploaded successfuly!’);
}
Вот эта функция предполагает вывод всех статусов на экран пользователя. Однако плагин этого не делает.
Уважаемый автор, Почему? Как мне сделать так, чтобы JS у пользователя получал сообщения как в стандартных Ajax запросах.
function exit_status($str){
echo json_encode(array(‘status’=>$str));
exit;
}
статья супер. еще бы добавить сжатие картинки на стороне клиента.