Вебдванольная загрузка фотографий без flash и html5

в 3:53, , рубрики: html, iframe, javascript, php, uploader, Веб-разработка, метки: , , , ,

Вебдванольная загрузка фотографий без flash и html5

Данный топик предназначен для веб-разработчиков начального и среднего уровня. Однако есть интересные моменты, которые могут заинтересовать и продвинутых девелоперов. Если вам интересно создание простого, но красивого загрузчика — добро пожаловать под хабракат.

Итак, небольшое отступление:

Зачем?

Загрузка файлов на сервер издавна была головной болью многих разработчиков. Самый популярный на сегодняшний день метод загрузки — это flash-загрузчик. Думаю, не стоит говорить о минусах такого подхода. Но выбор не богат — ведь AJAX не позволяет загружать файлы (не знаю, как с этим у HTML5, но в Javascript такой возможности нет). Однако, есть ещё один метод, о котором некоторые разработчики забывают…

Кто, если не flash?

Вспомним на секунду, как ведут себя обычные формы. Они позволяют загружать файлы, но перезагружают текущую страницу полностью… Или не полностью? Да, вы правы — только текущий frame. Тут то на помощь нам и приходит чудо вебстроительной мысли — iframe. Казалось бы, встроить форму в него, и всё.

Но если хочется красиво?

Хотим красиво — делаем красиво, нет ничего проще.
Для начала, подготовим каркас: страница и вывод уже загруженных фотографий.

<html>
<head>
<title>Photo uploader</title>
<style type="text/css">
	#photos a img{
		border: none; /* Для IE */
	}
	#photos div {
		width: 100px; /* Задаём размеры */
		height: 75px;
		float: left; /* Выстраиваем в ряд */
		margin: 3px; /* Немного отступов для красоты */
	}
	#photos {
		clear: both; /* Компенсация float */
	}	
</style>
</head>
<body>
<div id="photos">
<?php
	$dir = "photos/"; // Наша папка с фотографиями
	if(is_dir($dir)){ // Проверяем, существует ли она
		$files = scandir($dir); // Получаем список файлов
		foreach($files as $f){ // И перебираем их
			if(!preg_match('/^(d+).png$/i',$f,$m)) continue; // Если имя файла не имеет вид <число>.png, пропускаем этот файл. Заодно получаем номер в массиве $m
			$t = $dir.$m[1].'_thumb.png'; // Собираем имя уменьшенной картинки
			if(file_exists($t)) // И, если она существует - выводим
				echo '<div><a href="'.$dir.$f.'"><img src="'.$t.'" alt="photo"/></a></div>';
		}
	}
?>
</div>
</body>
</html>

Итак, каркас есть. Теперь создадим папку photos в папке с нашим скриптом, и заранее выставим для неё права на запись. Теперь, если скидывать в неё картинки с именами вида 1.png (сама фотография в полном размере) + 1_thumb.png (превью фото), они будут появляться на нашей странице, и делать это, в общем то, красиво:

Вебдванольная загрузка фотографий без flash и html5

Загрузка фотографий

Теперь сделаем скрипт, сохраняющий фотографию и превью для неё в автоматическом режиме (именно на него будут поступать запросы), заодно являющийся кнопкой "+":

<html>
<head>
<script type="text/javascript">
<?php
if(isset($_FILES, $_FILES['image'])){ // Если нам передан файл
	$f = $_FILES['image']; // То работаем с ним
	// Определяем тип и создаём изображение из временного файла
	if(preg_match('/.png$/i',$f['name']))
		$i = @ImageCreateFromPng($f['tmp_name']);
	if(preg_match('/.jp(e?)g$/i',$f['name']))
		$i = @ImageCreateFromJpeg($f['tmp_name']);
	if(preg_match('/.gif$/i',$f['name']))
		$i = @ImageCreateFromGif($f['tmp_name']);
	if(isset($i)||!!$i)  { // Если мы успешно получили и обработали файл, то сохраняем его
		$id = 1; while(file_exists('photos/'.$id.'.png')) $id++; // Получаем свободный номер
		ImagePng($i,'photos/'.$id.'.png'); // Сохраняем оригинал с этим номером
		$width = ImageSX($i); // Получаем размеры
		$height = ImageSY($i);
		$percent = min(100 / $width, 75 / $height); // Вычисляем, насколько надо ужать фото
		// Я выбрал максимальные размеры в 100х75 пикселей
		$new_width = $width * $percent;
		$new_height = $height * $percent;
		$t = imagecreatetruecolor($new_width, $new_height); // Создаём изображение
		imagecopyresampled($t, $i, 0, 0, 0, 0, $new_width, $new_height, $width, $height); // Копируем оригинал в нужном масштабе
		imagepng($t, 'photos/'.$id.'_thumb.png'); // Сохраняем превью
		$result = array( // Наши имена файлов для передачи основному скрипту
			'img'=>'photos/'.$id.'.png',
			'thumb'=>'photos/'.$id.'_thumb.png'
		);
		echo "window.top.photoadd(".json_encode($result).");"; // Передаём данные основной странице
	}
}
?>
	$ = function(i){ return document.getElementById(i);} // Получаем элемент по его ID
	function onformsubmit(){ // При начале загрузки
		$('b').innerHTML = 'uploading'; // заменяем текст и его размер
		$('b').style.fontSize = '14px';
		$('b').onclick = false; // и блокируем кнопку от повторного нажатия
	}
</script>
<style type="text/css">
	body {
		width: 100%; // Растягиваем файл на весь iframe
		height: 100%;
		margin: 0;
		padding: 0;
		background: #aaa; // Цвет фона и текста кнопки - по вкусу
		color: #444;
	}
	#form, #file {
		position: absolute;
		left: -1000;
		top: -1000; // Прячем форму - нам она не нужна
	}
	#b {
		width: 100%; // Наша кнопка на весь iframe
		height: 100%;
		vertical-align: middle; // Текст в центре
		text-align: center;
		cursor: pointer; // Курсор-указатель
		font-size: 60px; // Размер текста
		font-weight: bold; // И его жирность
	}
</style>
</head>
<body>
	<div id="b" onclick="$('file').click()">+</div>
	<form id="form" enctype="multipart/form-data" method="post" action="" onsubmit="onformsubmit()">
		<input id="file" onchange="$('form').submit()" type="file" name="image"/>
	</form>
</body>
</html>
Почти готово!

Осталось добавить на основную страницу iframe и написать обработчик для добавления фото:

<script>
	$ = function(i){ return document.getElementById(i);}
	window.photoadd = function(i){
		if(i.img && i.thumb){ // Если переданы ссылки
			with($('photos').insertBefore(document.createElement('div'), $('iframe'))){
				// Добавляем новую фотографию
				var a = appendChild(document.createElement('a'));
				a.href = i.img;
				a.target = '_blank';
				var im = a.appendChild(document.createElement('img'));
				im.src = i.thumb;
				im.alt = 'photo';
			}
		}
	}
</script>
<style type="text/css">
	#iframe iframe {
		width: 100px; /* Размеры */
		height: 75px;
		border: none; /* Убираем границы */
		overflow: hidden; /* И прокрутку тоже */
	}
</style>
<div id="photos">
<?php
	// ... наш вывод фотографий
?>
<div id="iframe"><iframe src="uploader.php"></iframe></div>
</div>

Вот и всё!

Демонстрация

По понятным причинам, давать ссылку на свой недо-сервачок я не буду — он ляжет, не дожив даже до начала хабраэффекта. Да и бесплатный фотохостинг из своего сайта делать не хочу. Однако, скрипт рабочий, и собрать его по частям довольно просто. Так что любой желающий сможет проверить его на своей локальной машине.

На самом деле (кроссбраузерность)

Firefox, Chrome, Opera, Safari всех версий реагируют на происходящее прекрасно.
Мобильный браузер Android тоже сделал всё правильно.
А вот (не будем говорить кто, хотя это и так понятно) генерирует где-то тонны полторы кирпичей и отказывается выполнять submit у формы. Диалог, к слову, даёт открыть, и позволяет выбрать файл, но дальше просто ничего не делает. Ну да и фиг с ним.

Автор: DjPhoeniX


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js