В движках интернет магазинов, часто при добавлении товара и загрузке его фото, на сервере сразу генерируются превью разных размеров для вывода в каталоге, карточках товаров, слайдерах и т.д. При данном подходе возникают проблемы – приходится перегенерировать все миниатюры при изменении размеров или при наложении водяных знаков.
Следующий приём позволяет этого избежать, работает он следующим образом:
- Оригинальное фото загружается в
/uploads/prods/originals/
, где prods – директория модуля (таких может быть несколько), originals – директория с оригинальными файлами (её следует закрыть от прямого доступа). - В тегах
<img>
, используются URL вида:/uploads/prods/100x100/image.jpg
с нужным размером, для изображений с исходным размером –/uploads/module/image.jpg
. - В .htaccess прописывается правило, которое запускает PHP-скрипт генерации превью из директории
/uploads/prods/originals/
если запрашиваемый файл ещё не сгенерирован.
Получается так, что изображение с нужным размером генерируется только один раз и для перегенерации достаточно удалить файлы или директории с ними (например /uploads/prods/100x100
).
В файле .htaccess
Правило проверят ведет ли текущий запрос на несуществующий файл и проверяет его расширение, если все сходится, то вызывается скрипт /auto-thumbs.php
.
В файле .htaccess:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} /uploads/.*?\.(jpg|jpeg|png|gif)$
RewriteRule ^(.*)$ auto-thumbs.php [L]
auto-thumbs.php
PHP-скрипт проверят разрешен ли запрашиваемый модуль и размер превью в массиве $config
, сделано это для предотвращения уязвимости при которой появляется возможность сгенерировать множество изображений с произвольными размерами и превысить дисковые лимиты. Далее скрипт генерирует изображение, если требуется накладывает watermark.png, сохраняет на сервере и отдает браузеру.
Для обработки изображений используется библиотека PHP Thumb, файл thumbs.php.
<?php
/* Название директории c файлами */
$dir = 'uploads';
/* Настройки размеров */
/* true - добавлять watermark */
$config = array(
'prods' => array(
'original' => true,
'300x200' => false,
'100x0' => false,
),
);
/* Разбор полученного URL */
$url = explode('?', trim($_SERVER['REQUEST_URI'], '/'));
$params = explode('/', $url[0]);
$module = @$params[1];
$size = @$params[2];
$file = @$params[3];
if ($params[0] == $dir && !empty($module) && array_key_exists($module, $config) && !empty($size)) {
if (empty($file)) {
$file = $size;
$size = 'original';
}
$info = pathinfo($file);
$path = __DIR__ . '/' . $dir . '/' . $module;
if (in_array($info['extension'], array('jpg', 'jpeg', 'png', 'gif')) && is_file($path . '/originals/' . $file)) {
if ($size == 'original') {
/* Запрос на оригинальное изображение(/uploads/prods/image.png) */
copy($path . '/originals/' . $file, $path . '/' . $file);
if (!empty($config[$module][$size])) {
/* Наложение watermark */
require_once __DIR__ . '/thumbs.php';
$image = new Thumbs($path . '/' . $file);
$image->watermark(__DIR__ . '/watermark.png', 'center', 1);
$image->save();
}
/* Вывод в браузер */
$img_info = getimagesize($path . '/' . $file);
switch ($img_info[2]) {
case 1: header('Content-Type: image/gif'); break;
case 2: header('Content-Type: image/jpeg'); break;
case 3: header('Content-Type: image/x-png'); break;
}
header('Content-Length: ' . filesize($path . '/' . $file));
readfile($path . '/' . $file);
exit();
} elseif (array_key_exists($size, $config[$module])) {
/* Запрос на превью (/uploads/prods/300x200/image.jpg) */
$sizes = explode('x', $size);
if (!is_dir($path . '/' . $size)) {
mkdir($path . '/' . $size, 0777, true);
}
/* Создание миниатюры */
require_once __DIR__ . '/thumbs.php';
$image = new Thumbs($path . '/originals/' . $file);
$image->thumb(intval($sizes[0]), intval($sizes[1]));
if (!empty($config[$module][$size])) {
$image->watermark(__DIR__ . '/watermark.png', 'center', 1);
}
/* Сохранение и вывод в браузер */
$image->saveOut($path . '/' . $size . '/' . $file);
exit();
}
}
}
header('HTTP/1.0 404 Not Found');
exit();
Использование на сайте
Вывод изображения шириной 100px и пропорциональной высотой:
Результат:
Оригинальный размер с водяным знаком:
Результат:
Со временем, директории с размерами будут заполнятся сгенерированными миниатюрами: