Кешируем превью в Gallery

Одно из самых популярных расширений MODX Revolution для работы с фото галереями это Gallery. На одном из сайтов мы его использовали для управления альбомами с большим количеством фотографий в них - одновременно на странице было необходимо отобразить около 100 изображений. И тут начала проседать производительность Gallery на этапе генерации превью.

Для просмотра уменьшенной копии изображения разработчики Gallery предлагают нам воспользоваться специальным коннектором, при этом для каждой картинки используется примерно такой URL:

assets/components/gallery/connector.php?action=web/phpthumb&h=100&w=100&zc=1&far=C&src=%2Fassets%2Fgallery%2F3%2F14.jpg

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

В качестве примера скажем, что отображение страницы, на которой 100 изображений, занимает более 10 секунд при первом запросе и около 1 секунды при повторных запросах уже кешированной страницы.

Создание файлов кеша в момент загрузки изображений

Итак, для ускорения рендеринга страницы и уменьшения нагрузки на сервер, попробуем создавать кеш изображений в момент загрузки файлов на сервер, а не в момент отображения.

Как оказалось сделать это не очень сложно, но потребуется редактирование файлов компонента Gallery.

Нам потребуется внести изменения в файл

/core/components/gallery/model/galitem.class.php

Добавим в начало класса galItem приватный член:

private $config = array(
  '.thumb' => array('w' => 100,'h' => 100,'zc' => 0,'bg' => '#fff','q' => 70, 'far' => 'C'),
  '.200' => array('w' => 200,'h' => 200,'zc' => 1,'q' => 90),
  // вы можете добавить сюда сколько угодно строк с настройками
);

Этот двумерный массив содержит настройки генерации превью для phpThumb. Аналогичный прием используется в плагине ResizeImageOnUpload.

Первый ключ .thumb служит для генерации превью для админки, второй (и следующие) - пользовательские, вы можете указать здесь любые настройки.

Далее в этом же классе galItem ищем метод save и добавляем в конец (перед return $saved;) следующий код:

// имя загруженного файла
$fn = $this->get('filename');
if(!empty($fn)){
  // подключаем phpthumb
  require_once MODX_CORE_PATH.'model/phpthumb/phpthumb.class.php';
  // для упрощения задачи путь к загружаемым файлам прописан жестко, что не совсем верно
  $fileName = MODX_BASE_PATH.'assets/gallery/'.$fn;
  // бежим по ключам настроек
  foreach($this->config as $cfgKey => $cfg){
    $thumbName = MODX_BASE_PATH.'assets/gallery/'.$cfgKey.'/'.$fn;
    $thumbDir = dirname($thumbName);
    if(!is_dir($thumbDir)){ mkdir($thumbDir, 0777, true); }
     
    // проверяем, что файла превью еще нет
    if(!file_exists($thumbName)){
      $phpThumb = new phpThumb();
      $phpThumb->setSourceFilename($fileName);
      // задаем настройки ресайза изображения
      foreach ($cfg as $k => $v) {
        $phpThumb->setParameter($k, $v);
      }
      // генерируем превью и сохраняем
      if ($phpThumb->GenerateThumbnail()) {
        if ($phpThumb->RenderToFile($thumbName)) {
          // из-за специфических настроек сервера необходимо установить права на новый файл
          chmod($thumbName, 0666);
        }
      }
    }
  }
}

Если теперь загрузить любое изображение в Gallery, то мы увидим такую картинку:

Gallery thumbs

Файлы превью создаются в каталоге .thumb, при этом файловая структура дублируется.

Удаление файлов кеша при удалении исходного изображения

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

Для этого внесем изменения в метод remove в этом же файле galitem.class.php. После удаления основного файла добавляем код для удаления файлов превью, в итоге функция должна выглядеть примерно так:

public function remove(array $ancestors = array()) {
  $filename = $this->get('filename');
  if (!empty($filename)) {
    $filename = $this->xpdo->call('galAlbum','getFilesPath',array(&$this->xpdo)).$filename;
    if (!@unlink($filename)) {
      $this->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Gallery] An error occurred while trying to remove the attachment file at: '.$filename);
    }
    // удаление превью
    foreach($this->config as $cfgKey => $cfgVal){
      $thumbFileName = str_replace('/gallery/', 'gallery/'.$cfgKey, $filename);
      if (!@unlink($thumbFileName)) {
        $this->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Gallery] An error occurred while trying to remove the attachment file at: '.$thumbFileName);
      }
    }
  }
  return parent::remove($ancestors);
}

Отображение кешированных изображений превью

Осталось показать наши кешированные изображения взамен стандартного connector. В админке все просто, ищем функцию "get" все в этом же файле, и после "case 'thumbnail':" добавляем:

return $this->getSiteUrl().'/assets/gallery/.thumb/'.$this->get('filename');

Проверям и убеждаемся, что админка заработала ощутимо шустрее.

Теперь дело за сайтом. Прежде всего потребуется небольшой modifier, т.е. обычный сниппет galleryThumb:

if(empty($options)){
  return $input;
}
return str_replace('/gallery/', '/gallery/'.$options.'/', $input);

Пример его вызова в чанке thumbTpl (см. http://rtfm.modx.com/display/ADDON/Gallery.Gallery.thumbTpl):

<img src="[[+image_absolute:galleryThumb=`.200`]]" />

В качестве параметра передаем один из ключей массива, о котором речь шла в самом начале статьи.

Вот и все, на выходе сгенерируется путь к картинке такого вида: assets/gallery/.200/5/123.jpg

Теперь отображение фотоальбома с большим количеством изображений происходит ощутимо быстрее как при первой загрузке страницы, так и при последующих.