Генерация превьюшек изображений на лету

Генерация превьюшек изображений на лету

В движках интернет магазинов, часто при добавлении товара и загрузке его фото, на сервере сразу генерируются превью разных размеров для вывода в каталоге, карточках товаров, слайдерах и т.д. При данном подходе возникают проблемы – приходится перегенерировать все миниатюры при изменении размеров или при наложении водяных знаков.

Следующий приём позволяет этого избежать, работает он следующим образом:

  1. Оригинальное фото загружается в /uploads/prods/originals/, где prods – директория модуля (таких может быть несколько), originals – директория с оригинальными файлами (её следует закрыть от прямого доступа).
  2. В тегах <img>, используются URL вида: /uploads/prods/100x100/image.jpg с нужным размером, для изображений с исходным размером – /uploads/module/image.jpg.
  3. В .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]
htaccess

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();
PHP

Использование на сайте

Вывод изображения шириной 100px и пропорциональной высотой:

<img src="/uploads/prods/100x0/image.jpg">
HTML

Результат:

Вывод изображения шириной 100px и пропорциональной высотой

Оригинальный размер с водяным знаком:

<img src="/uploads/prods/image.jpg">
HTML

Результат:

Оригинальный размер с водяным знаком

Со временем, директории с размерами будут заполнятся сгенерированными миниатюрами:

Со временем, директории с размерами будут заполнятся сгенерированными миниатюрами
07.08.2020
6390
Предыдущая запись Стилизация Checkbox

Комментарии 1

Евгений Мамаев Евгений Мамаев
5 декабря 2023 в 15:38
А есть что-нибудь для создания превьюшек видео?

, чтобы добавить комментарий.

Другие публикации

PHP-класс для создания миниатюр изображений
PHP Thumb – это библиотека, основанная на PHP GD и предназначена для создания превью изображений для каталогов, товаров...
49434
+13
Ускорение загрузки изображений и скриптов
Современные браузеры имеют поддержку предварительной загрузки ресурсов, указав в head страницы теги link с их адресами...
16545
+3
Галерея из плиток заполняющая всю ширину
Сделать галерею которая заполнит все пространство фотографиями разного размера можно с помощью jQuery плагина...
10373
+4
Автоматическое сжатие и оптимизация картинок на сайте
Изображения нужно сжимать для ускорения скорости загрузки сайта, но как это сделать? На многих хостингах нет...
28478
+8
Изображения WebP в GD PHP
WebP – формат сжатия изображений, разработанный Google. Имеет более меньший размер файла по сравнению с JPG, но не поддерживается продуктами Apple.
19200
+6
WebP вместо изображений в браузерах где он поддерживается
В данной статье приведены примеры как сделать подгрузку WebP вместо jpg и png в тех браузерах где он поддерживается.
12888
+5