Сегодня я хочу рассказать о реализации довольно популярной задачи. Во-первых, это загрузка изображения на сервер. А во-вторых, это изменение размера изображения. Также рассмотрим поворот и изменение качества.
Скачать исходный код себе на компьютер!
Немного теории по загрузке изображений на сервер средствами PHP
Вы не можете сразу загрузить файл в свою папку. Вначале он загружается во временную директорию сервера, а затем обрабатывается с помощью 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’] – код ошибки.
Подготовка
Для начала нам нужна форма для загрузки. Возьмём простейшую форму.
1 2 3 4 |
<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 запроса обработчик попробует осуществить загрузку файла по указанному пути. Скрипт сообщит о результате загрузки – удачна она или нет.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?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 и потренироваться с загрузкой файлов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?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" "//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> |
Проверки
Любая форма представляет для сайта опасность. И особенно форма загрузки файлов. Злоумышленник может загрузить скрипт и выполнить его на сервере. Поэтому необходимо озаботиться безопасностью.
Самые простые и обязательные проверки – на размер и тип файла. Для этого укажем допустимые типы и размер.
Тип файла укажем в виде массива:
1 |
$types = array('image/gif', 'image/png', 'image/jpeg'); |
а размер файла в байтах:
1 |
$size = 1024000; |
Проверяем тип файла. В случае недопустимого типа прекращаем работу скрипта и выводим соответствующее уведомление. Функция in_array проверяет присутствие значения в массиве.
1 2 3 |
// Проверяем тип файла if (!in_array($_FILES['picture']['type'], $types)) die('Запрещённый тип файла. <a href="?">Попробовать другой файл?</a>'); |
Проверяем размер файла. В случае недопустимого размера прекращаем работу скрипта и выводим соответствующее уведомление.
1 2 3 |
// Проверяем размер файла if ($_FILES['picture']['size'] > $size) die('Слишком большой размер файла. <a href="?">Попробовать другой файл?</a>'); |
Итого получаем такой скрипт. Скрипт рабочий, можно баловаться. Немного забегая вперёд, добавим также параметр $tmp_path – путь к папке временных файлов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?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" "//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> |
Изменение размеров изображений PHP
Приступим к самому интересному, а именно изменению размеров изображения с помощью PHP. Для этого напишем функцию resize. Сделаем также возможным изменять качество изображения и поворачивать его.
Размер изображения будем подставлять исходя из параметра. Это будет либо эскиз ($type = 1), либо большое изображение ($type = 2).
Итак, шапка функции у нас получилась такая:
1 |
function resize($file, $type = 1, $rotate = null, $quality = null) |
По умолчанию подставляем размеры эскиза, а поворот и качество по умолчанию не используются. Пойдём дальше.
Устанавливаем ограничение размера изображения по ширине. Функцию можно сделать более абстрактной, если задавать эти значения в параметрах, а также сделать возможным ограничение и по высоте. Однако в данном случае нам не нужна такая универсальность.
1 2 |
$max_thumb_size = 200; $max_size = 800; |
Устанавливаем качество изображения по умолчанию (при $quality = null) равным 75%.
1 2 |
if ($quality == null) $quality = 75; |
Далее создаём изображение для дальнейших преобразований. Для создания используем функцию в зависимости от типа файла (jpg, png или gif). Функции создания называются очень лаконично imagecreatefrom + тип файла.
1 2 3 4 5 6 7 8 |
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 градусов.
1 2 |
if ($rotate != null) $src = imagerotate($source, $rotate, 0); |
Далее определяем высоту и ширину изображения с помощью функций imagesx и imagesy.
1 2 |
$w_src = imagesx($src); $h_src = imagesy($src); |
В зависимости от типа (эскиз или большое изображение) устанавливаем ограничение по ширине.
1 2 3 4 |
if ($type == 1) $w = $max_thumb_size; elseif ($type == 2) $w = $max_size; |
Далее, если ширина изображения больше максимальной, проводим преобразования. Иначе просто сохраняем изображение и очищаем память. Сохраняем изображение с помощью функции imagejpeg. В данном примере, рассмотрено сохранение только в формате jpg, однако функционал всегда можно расширить. Удаляем изображения из памяти с помощью функции imagedestroy.
В качестве результата работы функции возвращаем имя файла. Оно нам ещё понадобится.
1 2 3 4 5 6 7 8 9 10 11 |
if ($w_src > $w) { // преобразования } else { imagejpeg($src, $tmp_path . $file['name'], $quality); imagedestroy($src); return $file['name']; } |
Вернёмся к преобразованию. Вначале вычисляем пропорции изображения и размеры преобразованного изображения.
1 2 3 |
$ratio = $w_src/$w; $w_dest = round($w_src/$ratio); $h_dest = round($h_src/$ratio); |
Далее создаём пустую картинку (функция imagecreatetruecolor) с шириной и высотой, полученными на прошлом шаге.
1 |
$dest = imagecreatetruecolor($w_dest, $h_dest); |
И копируем исходное изображение ($src) в только что созданное ($dest), изменяя его размеры. Функция imagecopyresampled делает это с пересэмплированием, что улучшает качество.
1 |
imagecopyresampled($dest, $src, 0, 0, 0, 0, $w_dest, $h_dest, $w_src, $h_src); |
И наконец, сохраняем полученное изображение и очищаем память.
1 2 3 |
imagejpeg($src, $tmp_path . $file['name'], $quality); imagedestroy($dest); imagedestroy($src); |
Итого, функция получает исходное изображение и параметры преобразования, выполняет преобразования, сохраняет полученный файл во временную папку и возвращает имя изображения. Теперь нам осталось только переложить файл в конечную папку.
Отвечу заранее на вопрос «Почему мы не можем сразу положить изменённый файл в конечную папку?». Можем. Однако не делаем для увеличения глубины абстракции, то есть, чтобы придать определённую универсальность функции. Вы же сможете её использовать на разных сайтах.
Совсем забыл. Добавляем в начало функции строку:
1 |
global $tmp_path; |
Она обозначает, что в функции будет использована глобальная переменная $tmp_path – путь к временной папке.
Вызов функции
Функцию нужно вызывать сразу после проверок. А также следует изменить ту часть скрипта, где мы копируем изображение в конечную папку. Вы ведь теперь работаем с новым изображением. Теперь схема загрузки такова: компьютер → временная папка сервера → наша временная папка → конечная папка. То есть, добавился ещё один промежуточный пункт.
Для пущего веселья добавим в нашу форму выпадающий список, чтобы мы устанавливать тип загрузки и поля для ввода градуса поворота.
В таком случае вызов функции будет такой:
1 |
$name = resize($_FILES['picture'], $_POST['file_type'], $_POST['file_rotate']); |
Конечный результат
И наконец, конечный результат.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "//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> |
Послесловие
Естественно, рассмотренный пример учебный. Однако он вполне рабочий. Что вы можете попробовать, забросив скрипт на сервер и создав папки для изображений и временных файлов. Скрипт можно бесконечно дорабатывать, изменять уровень абстракции, добавлять условия и параметры, преобразования, проверки, накладывать «водяной знак».
Идеальный вариант – осмыслить и допилить до своих требований.
Домашнее задание
Конечно, задание проверять никто не будет. Однако я рекомендую его выполнить для себя. Ведь программирование – это, прежде всего, практика. Итак:
Вынесите размеры эскиза ($max_thumb_size) и большого изображения ($max_size) из функции в настройки файла.
Попробуйте задавать их в параметрах функции. Это значительно повысит уровень абстракции.
Проработайте вариант, когда изображение ограничивается по большей стороне, а не только по ширине.
Попробуйте получить на выходе файл того же типа, что и исходный. То есть, например, если загружаете gif, то и уменьшенная копия будет gif.
Попробуйте генерировать и эскиз и большой файл за одну загрузку.
Попробуйте наложить «водяной знак».
Используя бонусный код, добавьте в функцию возможность создания квадратных файлов. Добавьте в форму выбор типа обрезки – квадратная или пропорциональная.
Удачи в разработках!
P.S. Если вы захотите генерировать не пропорциональное изображение, а вырезать серединку и делать квадратную картинку, вам поможет этот код. Вставить его в функцию resize вы должны сами. Уверен, что у вас получится.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Создаём пустую квадратную картинку $dest = imagecreatetruecolor($w, $w); // Вырезаем квадратную серединку по x, если фото горизонтальное if ($w_src > $h_src) imagecopyresampled($dest, $src, 0, 0, round((max($w_src, $h_src) - min($w_src, $h_src))/2), 0, $w, $w, min($w_src, $h_src), min($w_src, $h_src)); // Вырезаем квадратную серединку по y, если фото горизонтальное elseif ($w_src < $h_src) imagecopyresampled($dest, $src, 0, 0, 0, round((max($w_src, $h_src) - min($w_src, $h_src))/2), $w, $w, min($w_src, $h_src), min($w_src, $h_src)); // Квадратная картинка масштабируется без вырезок elseif ($w_src == $h_src) imagecopyresampled($dest, $src, 0, 0, 0, 0, $w, $w, $w_src, $w_src); |
До следующих встреч, уважаемые читатели
Если у вас есть вопросы, по загрузке изображений на сервер и изменению размеров изображений средствами языка PHP, то пишите в комменатриях к статье, я обязательно отвечу!
Автор: Алексей Опанасенко
Редакция: Рог Виктор и Андрей Бернацкий. Команда webformyself.
E-mail: contact@webformyself.com
Проект webformyself.com — Как создать свой сайт. Основы самостоятельного сайтостроения
«Киберсант-вебмастер» — самый полный курс по сайтостроению в рунете!
P.S. Хотите опубликовать интересный тематический материал и заработать? Если ответ «Да», то жмите сюда.
Комментарии (83)