Загрузка изображений с изменением размера
Немного теории
Вы не можете сразу загрузить файл в свою папку. Вначале он загружается во временную директорию сервера, а затем обрабатывается с помощью PHP интерпритатора. По окончанию сессии временный файл автоматически удаляется. То есть, мы вначале забрасываем файл во временную папку, а затем перекладываем в нужную.
$_FILES это массив загруженных файлов. Он имеет параметры (на примере файла picture):
$_FILES[' picture ']['name'] – настоящее имя файла. Например: image.jpg.
$_FILES[' picture ']['size'] – размер файла в байтах.
$_FILES[' picture ']['type'] – MIME-тип загруженного файла. Например: image/gif, image/png, image/jpeg.
$_FILES[' picture ']['tmp_name'] – содержит имя файла во временном каталоге, например: /tmp/phpV3b3qY. Именно этот параметр и используется для перемещения файлов после загрузки.
$_FILES[' picture ']['error'] – код ошибки.
Подготовка
Для начала нам нужна форма для загрузки. Возьмём простейшую форму.
<form enctype="multipart/form-data" method="post">
<input name="picture" type="file" />
<input type="submit" value="Загрузить" />
</form>
Параметр enctype="multipart/form-data" обязателен для такой формы. Тег <input type="file" name="picture"> отвечает за поле для ввода имени файла, который загружается на сервер.
Также нам потребуется обработчик события – загрузки файла. Вначале у нас будет одна настройка – путь сохранения изображения. Можно указывать как прямой, так и относительный путь. В случае POST запроса обработчик попробует осуществить загрузку файла по указанному пути. Скрипт сообщит о результате загрузки – удачна она или нет.
<?php
$path = 'i/';
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
if (!@copy($_FILES['picture']['tmp_name'], $path . $_FILES['picture']['name']))
echo 'Что-то пошло не так';
else
echo 'Загрузка удачна';
}
?>
Функция copy, как вы наверно догадались, отвечает за копирование файла из одного место в другое. Мы копируем файл из временной папки сервера в нужную, сохранив имя файла.
Договоримся, что и форма и её обработчик будут находиться в одном файле – upload.php.
Итого имеем простой, но рабочий скрипт. Его можно забросить на хостинг, создать папку i и потренироваться с загрузкой файлов.
<?php
// Путь загрузки
$path = 'i/';
// Обработка запроса
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// Загрузка файла и вывод сообщения
if (!@copy($_FILES['picture']['tmp_name'], $path . $_FILES['picture']['name']))
echo 'Что-то пошло не так';
else
echo 'Загрузка удачна';
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Загрузка изображения с изменением размеров</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>Загрузка изображения с изменением размеров</h1>
<form method="post" enctype="multipart/form-data">
<input type="file" name="picture">
<input type="submit" value="Загрузить">
</form>
</body>
</html>
Проверки
Любая форма представляет для сайта опасность. И особенно форма загрузки файлов. Злоумышленник может загрузить скрипт и выполнить его на сервере. Поэтому необходимо озаботиться безопасностью.
Самые простые и обязательные проверки – на размер и тип файла. Для этого укажем допустимые типы и размер.
Тип файла укажем в виде массива:
$types = array('image/gif', 'image/png', 'image/jpeg');
а размер файла в байтах:
$size = 1024000;
Проверяем тип файла. В случае недопустимого типа прекращаем работу скрипта и выводим соответствующее уведомление. Функция in_array проверяет присутствие значения в массиве.
// Проверяем тип файла
if (!in_array($_FILES['picture']['type'], $types))
die('Запрещённый тип файла. <a href="?">Попробовать другой файл?</a>');
Проверяем размер файла. В случае недопустимого размера прекращаем работу скрипта и выводим соответствующее уведомление.
// Проверяем размер файла
if ($_FILES['picture']['size'] > $size)
die('Слишком большой размер файла. <a href="?">Попробовать другой файл?</a>');
Итого получаем такой скрипт. Скрипт рабочий, можно баловаться. Немного забегая вперёд, добавим также параметр $tmp_path – путь к папке временных файлов.
<?php
// Пути загрузки файлов
$path = 'i/';
$tmp_path = 'tmp/';
// Массив допустимых значений типа файла
$types = array('image/gif', 'image/png', 'image/jpeg');
// Максимальный размер файла
$size = 1024000;
// Обработка запроса
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// Проверяем тип файла
if (!in_array($_FILES['picture']['type'], $types))
die('Запрещённый тип файла. <a href="?">Попробовать другой файл?</a>');
// Проверяем размер файла
if ($_FILES['picture']['size'] > $size)
die('Слишком большой размер файла. <a href="?">Попробовать другой файл?</a>');
// Загрузка файла и вывод сообщения
if (!@copy($_FILES['picture']['tmp_name'], $path . $_FILES['picture']['name']))
echo 'Что-то пошло не так';
else
echo 'Загрузка удачна <a href="$path . $_FILES['picture']['name'] . '">Посмотреть</a> ' ;
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Загрузка изображения с изменением размеров</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>Загрузка изображения с изменением размеров</h1>
<form method="post" enctype="multipart/form-data">
<input type="file" name="picture">
<input type="submit" value="Загрузить">
</form>
</body>
</html>
Изменение размеров
Приступим к самому интересному. К изменению параметров изображения. Для этого напишем функцию resize. Сделаем также возможным изменять качество изображения и поворачивать его.
Размер изображения будем подставлять исходя из параметра. Это будет либо эскиз ($type = 1), либо большое изображение ($type = 2).
Итак, шапка функции у нас получилась такая:
function resize($file, $type = 1, $rotate = null, $quality = null)
По умолчанию подставляем размеры эскиза, а поворот и качество по умолчанию не используются. Пойдём дальше.
Устанавливаем ограничение размера изображения по ширине. Функцию можно сделать более абстрактной, если задавать эти значения в параметрах, а также сделать возможным ограничение и по высоте. Однако в данном случае нам не нужна такая универсальность.
$max_thumb_size = 200;
$max_size = 800;
Устанавливаем качество изображения по умолчанию (при $quality = null) равным 75%.
if ($quality == null)
$quality = 75;
Далее создаём изображение для дальнейших преобразований. Для создания используем функцию в зависимости от типа файла (jpg, png или gif). Функции создания называются очень лаконично imagecreatefrom + тип файла.
if ($file['type'] == 'image/jpeg')
$source = imagecreatefromjpeg ($file['tmp_name']);
elseif ($file['type'] == 'image/png')
$source = imagecreatefrompng ($file['tmp_name']);
elseif ($file['type'] == 'image/gif')
$source = imagecreatefromgif ($file['tmp_name']);
else
return false;
Если указан параметр $rotate, выполняем поворот изображения. Делаем это с помощью функции rotate(), параметрами которой являются: изображение, градусы, фон изображения для закрашивания пустых областей, образованных при повороте. Для того чтобы пустые области не возникали, поворачиваем изображение на угол в 90, 180, 270 градусов.
if ($rotate != null)
$src = imagerotate($source, $rotate, 0);
Далее определяем высоту и ширину изображения с помощью функций imagesx и imagesy.
$w_src = imagesx($src);
$h_src = imagesy($src);
В зависимости от типа (эскиз или большое изображение) устанавливаем ограничение по ширине.
if ($type == 1)
$w = $max_thumb_size;
elseif ($type == 2)
$w = $max_size;
Далее, если ширина изображения больше максимальной, проводим преобразования. Иначе просто сохраняем изображение и очищаем память. Сохраняем изображение с помощью функции imagejpeg. В данном примере, рассмотрено сохранение только в формате jpg, однако функционал всегда можно расширить. Удаляем изображения из памяти с помощью функции imagedestroy.
В качестве результата работы функции возвращаем имя файла. Оно нам ещё понадобится.
if ($w_src > $w)
{
// преобразования
}
else
{
imagejpeg($src, $tmp_path . $file['name'], $quality);
imagedestroy($src);
return $file['name'];
}
Вернёмся к преобразованию. Вначале вычисляем пропорции изображения и размеры преобразованного изображения.
$ratio = $w_src/$w;
$w_dest = round($w_src/$ratio);
$h_dest = round($h_src/$ratio);
Далее создаём пустую картинку (функция imagecreatetruecolor) с шириной и высотой, полученными на прошлом шаге.
$dest = imagecreatetruecolor($w_dest, $h_dest);
И копируем исходное изображение ($src) в только что созданное ($dest), изменяя его размеры. Функция imagecopyresampled делает это с пересэмплированием, что улучшает качество.
imagecopyresampled($dest, $src, 0, 0, 0, 0, $w_dest, $h_dest, $w_src, $h_src);
И наконец, сохраняем полученное изображение и очищаем память.
imagejpeg($src, $tmp_path . $file['name'], $quality);
imagedestroy($dest);
imagedestroy($src);
Итого, функция получает исходное изображение и параметры преобразования, выполняет преобразования, сохраняет полученный файл во временную папку и возвращает имя изображения. Теперь нам осталось только переложить файл в конечную папку.
Отвечу заранее на вопрос «Почему мы не можем сразу положить изменённый файл в конечную папку?». Можем. Однако не делаем для увеличения глубины абстракции, то есть, чтобы придать определённую универсальность функции. Вы же сможете её использовать на разных сайтах.
Совсем забыл. Добавляем в начало функции строку:
global $tmp_path;
Она обозначает, что в функции будет использована глобальная переменная $tmp_path – путь к временной папке.
Вызов функции
Функцию нужно вызывать сразу после проверок. А также следует изменить ту часть скрипта, где мы копируем изображение в конечную папку. Вы ведь теперь работаем с новым изображением. Теперь схема загрузки такова: компьютер → временная папка сервера → наша временная папка → конечная папка. То есть, добавился ещё один промежуточный пункт.
Для пущего веселья добавим в нашу форму выпадающий список, чтобы мы устанавливать тип загрузки и поля для ввода градуса поворота.
В таком случае вызов функции будет такой:
$name = resize($_FILES['picture'], $_POST['file_type'], $_POST['file_rotate']);
Конечный результат
И наконец, конечный результат.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Загрузка изображения с изменением размеров</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>Загрузка изображения с изменением размеров</h1>
<?php
// Пути загрузки файлов
$path = 'i/';
$tmp_path = 'tmp/';
// Массив допустимых значений типа файла
$types = array('image/gif', 'image/png', 'image/jpeg');
// Максимальный размер файла
$size = 1024000;
// Обработка запроса
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// Проверяем тип файла
if (!in_array($_FILES['picture']['type'], $types))
die('<p>Запрещённый тип файла. <a href="?">Попробовать другой файл?</a></p>');
// Проверяем размер файла
if ($_FILES['picture']['size'] > $size)
die('<p>Слишком большой размер файла. <a href="?">Попробовать другой файл?</a></p>');
// Функция изменения размера
// Изменяет размер изображения в зависимости от type:
// type = 1 - эскиз
// type = 2 - большое изображение
// rotate - поворот на количество градусов (желательно использовать значение 90, 180, 270)
// quality - качество изображения (по умолчанию 75%)
function resize($file, $type = 1, $rotate = null, $quality = null)
{
global $tmp_path;
// Ограничение по ширине в пикселях
$max_thumb_size = 200;
$max_size = 600;
// Качество изображения по умолчанию
if ($quality == null)
$quality = 75;
// Cоздаём исходное изображение на основе исходного файла
if ($file['type'] == 'image/jpeg')
$source = imagecreatefromjpeg($file['tmp_name']);
elseif ($file['type'] == 'image/png')
$source = imagecreatefrompng($file['tmp_name']);
elseif ($file['type'] == 'image/gif')
$source = imagecreatefromgif($file['tmp_name']);
else
return false;
// Поворачиваем изображение
if ($rotate != null)
$src = imagerotate($source, $rotate, 0);
else
$src = $source;
// Определяем ширину и высоту изображения
$w_src = imagesx($src);
$h_src = imagesy($src);
// В зависимости от типа (эскиз или большое изображение) устанавливаем ограничение по ширине.
if ($type == 1)
$w = $max_thumb_size;
elseif ($type == 2)
$w = $max_size;
// Если ширина больше заданной
if ($w_src > $w)
{
// Вычисление пропорций
$ratio = $w_src/$w;
$w_dest = round($w_src/$ratio);
$h_dest = round($h_src/$ratio);
// Создаём пустую картинку
$dest = imagecreatetruecolor($w_dest, $h_dest);
// Копируем старое изображение в новое с изменением параметров
imagecopyresampled($dest, $src, 0, 0, 0, 0, $w_dest, $h_dest, $w_src, $h_src);
// Вывод картинки и очистка памяти
imagejpeg($dest, $tmp_path . $file['name'], $quality);
imagedestroy($dest);
imagedestroy($src);
return $file['name'];
}
else
{
// Вывод картинки и очистка памяти
imagejpeg($src, $tmp_path . $file['name'], $quality);
imagedestroy($src);
return $file['name'];
}
}
$name = resize($_FILES['picture'], $_POST['file_type'], $_POST['file_rotate']);
// Загрузка файла и вывод сообщения
if (!@copy($tmp_path . $name, $path . $name))
echo '<p>Что-то пошло не так.</p>';
else
echo '<p>Загрузка прошла удачно <a href="' . $path . $_FILES['picture']['name'] . '">Посмотреть</a>.</p>';
// Удаляем временный файл
unlink($tmp_path . $name);
}
?>
<form method="post" enctype="multipart/form-data">
<input type="file" name="picture">
<br>
<label>Тип загрузки</label>
<br>
<select name="file_type">
<option value="1">Эскиз</option>
<option value="2">Большое изображение</option>
</select>
<br>
<label>Поворот</label>
<br>
<input type="text" name="file_rotate">
<br>
<input type="submit" value="Загрузить">
</form>
</body>
</html>