Search
Write a publication
Pull to refresh

Вуду mod_rewrite и умные превьюшки

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

Традиционно программисты на PHP следуют наиболее легким путем. Создается файл preview.php, в котором записывается алгоритм преобразования картинки, URL которой передается в запросе, с помощью функций GD (а именно imagecopyresampled) и вывода его в браузер с выдачей соответствующих http-заголовков. Затем в HTML-коде такая масштабированная картинка вызывается с помощью примерно такого кода: <img src=«preview.php?image=test.jpg&width=100»… />
Примеры кода не привожу, поскольку их можно взять практически в любой CMS.

Такой подход имеет множество недостатков, самый главный из них — это необходимость запуска интерпретатора PHP при каждом запросе масштабированного изображения. Во что это может вылиться при отображении, скажем, страницы интернет-магазина с несколькими десятками товаров, наверное не стоит пояснять.

При создании своего скрипта интернет-магазина, а затем и своей CMS я попытался найти другой путь. Вот что получилось.


1. Для показа урезанного изображения вместо исходного, достаточно написать test.jpg.preview вместо test.jpg
2. Если требуется задать конкретную ширину или высоту превьюшки, можно использовать конструкции такого вида: test.jpg.w200.preview или test.jpg.h100.preview или даже test.jpg.w100.h20.preview (в этом случае масштабирование будет непропорциональным)
3. Обращение к PHP происходит только один раз, в момент создания превью, в последующем картинка берется из файловой системы.

Для работы нам понадобятся:
— модуль mod_rewrite в составе Apache, разрешенная обработка файлов .htaccess
— PHP, собранный с библиотекой GD

Итак, создаем, если он не существует, файл .htaccess в корне нашего веб-приложения.
В него вносим следующие строки:

RewriteEngine On — включаем модуль mod_rewrite
RewriteRule ^.htaccess$ - [F] — в целях безопасности запрещаем доступ к самому файлу .htaccess

RewriteCond %{REQUEST_URI} ^(.*?)\.(w([1-9][0-9]*)\.)?(h([1-9][0-9]*)\.)?preview$
RewriteCond %{DOCUMENT_ROOT}/%1 !-s
RewriteRule (.*) images/noimage.gif [L]

Это уже немного сложнее. В первой строчке мы проверяем, действительно в URL-е запрашивается превью по заданной нами схеме и одновременно разбиваем строку запроса на три части — имя файла, ширина и высота.
Во второй строке мы проверяем, существует ли в реальности файл, превью которого мы хотим показать, и если нет — то третьей строкой выводим вместо него в браузер файл noimage.gif, прекращая дальнейшую работу mod_rewrite

RewriteCond %{REQUEST_URI} ^(.*?)\.(w([1-9][0-9]*)\.)?(h([1-9][0-9]*)\.)?preview$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) previewimage.php?image=%1&width=%3&height=%5 [L]

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

Привожу содержимое файла previewimage.php целиком:
<?php

$path = ROOT_PATH . "/" . $_GET['image'];
$path_parts = pathinfo($path);
$extension = strtolower($path_parts['extension']);

$width = intval($_GET['width']);
$height = intval($_GET['height']);

$cache_ext = (!empty($width) ? ".w".$width : "") . (!empty($height) ? ".h".$height : "") . ".preview";

if (!(file_exists($path . $cache_ext))) {
TPCMS::makePreview($path, $extension, $width, $height);
};

switch ($extension) {
case "gif":
header("Content-type: image/gif");
break;
case "jpeg":
case "jpg":
header("Content-type: image/jpeg");
break;
case "png":
header("Content-type: image/png");
break;
};

@readfile($path . $cache_ext);
exit();

?>


ROOT_PATH — это абсолютный путь к корню веб-приложения в файловой системе.

Вся суть здесь — в используемой мной функции TPCMS::makePreview(), которая и строит масштабированную картинку и записывает ее в файловую систему для дальнейшего использования.

Также приведу ее целиком:
class TPCMS {

function makePreview($path, $ext, $width=0, $height=0) {

define("CORE_PREVIEW_DEFAULT_WIDTH", 100);

$image = imagecreatefromstring(file_get_contents($path));
$old_x = imagesx($image);
$old_y = imagesy($image);

if ( !empty($width) && !empty($height) ) {

$new_x = $width;
$new_y = $height;

} elseif ( !empty($width) ) {

$new_x = $width;
$new_y = intval($old_y * $new_x / $old_x);

} elseif ( !empty($height) ) {

$new_y = $height;
$new_x = intval($old_x * $new_y / $old_y);

} else {

$new_x = CORE_PREVIEW_DEFAULT_WIDTH;
$new_y = intval($old_y * $new_x / $old_x);

};

$new_image = imagecreatetruecolor($new_x, $new_y);
imagecopyresampled($new_image, $image, 0, 0, 0, 0, $new_x, $new_y, $old_x, $old_y);

$cache_ext = (!empty($width) ? ".w".$width : "") . (!empty($height) ? ".h".$height : "") . ".preview";

switch ($ext) {

case "gif":
imagegif($new_image, $path . $cache_ext);
break;

case "jpeg":
case "jpg":
imagejpeg($new_image, $path . $cache_ext);
break;
case "png":
imagepng($new_image, $path . $cache_ext);
break;

}

}

}


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

Остается только один шаг — определить момент, когда нужно очистить кэш, то есть удалить с диска все файлы, имя которых заканчивается на ".preview". Но это уже зависит от конкретного приложения и эту работу каждый сделает самостоятельно.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.