Интерактивная загрузка файлов на HTML5 и jQuery

12-10-30 Html, JavaScript HTML, jQuery 3

Сегодня мы сделаем небольшое веб-приложение, которое позволит пользователям загружать фотографии со своего компьютера, просто перетаскивая их в окно браузера. Это возможно благодаря новым возможностям HTML5.

У фото в нашем приложении будут небольшие иконки и индикатор загрузки, которые будут создаваться на стороне клиента. Пока изображения просто хранятся в папке на сервере, но вы можете расширить функционал.

HTML5 загрузка файлов

Для начала разберемся что такое загрузка с помощью HTML5. На самом деле это комбинация трех новых технологий: File Reader API,  Drag & Drop API и AJAX. Рассмотрим процесс загрузки:

  1. Пользователь перетаскивает один или несколько файлов в окно браузера. Браузер, поддерживающий Drag & Drop API генерирует событие, которое наряду с другой информацией содержит список файлов.
  2. Используя File Reader API, мы считываем файлы и запоминаем их.
  3. Используя новый метод sendAsBinary объекта XMLHttpRequest, мы отправляем файлы на сервер.

Все эти шаги сделает за нас jQuery плагин Filedrop. Плагин позволяет нам задавать максимальный размер файлов и определять функции обратного вызова, что очень полезно для встраивания в ваше веб приложение.

Минус данного метода в том, что он работает не во всех браузерах. Хотя последние версии поддерживают данные функции, сделать стандартную форму загрузки не будет лишним. В нашем примере мы не будем уделять этому внимания.

HTML

Начнем с разметки нашего приложения. Это простая html5 страница, мы подключаем стили, script.jsFiledrop 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 мы получим интерактивный индикатор загрузки.

На этом всё! 🙂

Хочешь получать статьи на почту?

Подпишись на обновления!
* Ваш email не будет разглашен/продан. Вы сможете отписаться в любое время.

3 Комментария

  1. Алексей says:

    Здравствуйте! Спасибо большое за урок! Хотел бы спросить, есть ли возможность приделать удаления фотографий, после того как их перетащили и они видны в виде миниатюр?

  2. Crapiv says:

    Вот пример кода:

    // перемещаем файл из временной папки в папку хранения на сервере
    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;
    }

  3. Вадим says:

    статья супер. еще бы добавить сжатие картинки на стороне клиента.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *