Подготовка мини-копий картинок (thumbs) на лету
В этой статье я попробую реализовать генерирование уменьшенных копий на лету, то есть по первому запросу к картинке.
Подумал я однажды, чем каждый раз перегинерировать мини-копии картинок, можно перехватить запрос для несуществующей картинки и сгенерировать мини-копию с нужным размером на лету. И попробовал это реализовать. Расскажу и вам, как я это делал, уважаемые любители Symfony.
Файл .haccess в папке web имеет инструкцию -- если запрашиваемого файла не существует, то вызывается механизм Symfony, обрабатывающий этот запрос. Воспользуемся этим для создания мини-копий.
Создаю
Мне понадобится добавить в проект модуль, к примеру uploads:
symfony generate:module frontend uploads
В routing.yml добавляю путь, имеющий такой-же url, что и папка где я буду хранить мини-копии, в этом состоит трюк. В данном случае url /uploads/thumbs.
uploads_thumbs:
url: /uploads/thumbs/:size/:path
param: { module: uploads, action: thumbs }
Чтобы не писать паровоз для работы с графикой я использую прекрасный плагин для Symfony, sfThumbnailPlugin. Устанавливаю:
symfony plugin:install sfThumbnailPlugin
Приступаю к модулю, который был сгенерирован, добавляю процедуру создания уменьшенной копии в actions.class.php.
/**
* This action will generate thumbs, if it is not yet generated
* and cache them to uploads/thumbs folder
* @param sfWebRequest $request
*/
public function executeThumbs(sfWebRequest $request)
{
$path = $request->getParameter('path');
$size = $request->getParameter('size');
$dir = sfConfig::get('sf_upload_dir');
if ( !$path || !$size || !file_exists( "$dir/thumbs/$size" ) || !file_exists( "$dir/$path") ) $this->forward404();
if ( !file_exists( "$dir/thumbs/$path" ) )
{
$thumbnail = new sfThumbnail($size, $size);
$thumbnail->loadFile( "$dir/$path" );
$thumbnail->save( "$dir/thumbs/$size/$path", 'image/png');
}
return $this->redirect( $request->getUri() );
}
По тексту понятно, что если нет одного из необходимых параметров - пути или размера, или папка с нужным размером мини-копий не создана, то выводить ошибку. Если же всё нормально, то создать мини-копию, сохранить туда где лежат мини-копии и перенаправить запрос снова по тому-же самому пути. Но на сей раз он не будет обрабатываться Symfony потому как нужный файл есть. Вот и весь трюк!
Проверяю
Создам в папке web/uploads/thumbs несколько папок с размерами мини-копий, которые мне необходимо получать, например 150, 300, 500, ставлю этим папкам права 0777, кладу в web/uploads какую-нибудь тестовую картинку 1.jpg. Пробую запросы:
- http://localhost/my_web/uploads/thumbs/150/1.jpg
- http://localhost/my_web/uploads/thumbs/300/1.jpg
- http://localhost/my_web/uploads/thumbs/500/1.jpg
Но вот ничего не получается и я разбираюсь, в чём же дело.
Работа над ошибками
Первая проблема, это параметр :path, который определяется не полностью. Точка и jpg распознаётся как следующая за :path часть пути. Чтобы не было проблем с расширением файла, придётся немного допилить настройку routing. Делается это в factories.yml:
all:
routing:
class: sfPatternRouting
param:
generate_shortest_url: true
extra_parameters_as_query_string: true
segment_separators: ['/']
Здесь я измелил параметр segment_separators, который стандартно считает разделителями путей символы / и . Поскольку точка нам нужна для получения полного имени картинки, мы эту точку убираем из segment_separators. Если есть иной способ не считать точку разделителем, пожалуйста добавьте в комментарии к статье, я считаю что он есть (подсказка: with_wildcard_чтототам прямо в routing.yml, но я не помню точно).
Вторая проблема обычно вылазит на production хостинге, где папку web, как полагается, мы выносим из проекта отдельно. Указать пути к sf_web_dir и sf_upload_dir можно и другими способами, но я делаю это в settings.yml:
all:
web_dir: %SF_ROOT_DIR%/../my_web #было %SF_ROOT_DIR%/web
upload_dir: %SF_ROOT_DIR%/../my_web/uploads #было %SF_ROOT_DIR%/web/uploads
Отчищаю кэш symfony cc и пробую те же запросы сделать снова. Ура! Работает!
