Николай Ланец
4 мая 2013 г., 18:46

Оптимизация phpThumbOf

Наверняка много кто использовал на своих сайтах пакет phpThumbOf — отличная штука, чтобы сделать превьюшки. Простой пример в использовании:
[[phpthumbof?input=`[[+source]]`&options=`w=350&h=350&zc=1`]]

На лету создаст превьюшку 350 на 350.
Но с этим пакетом не все так просто.
Проблема первая. Чтение кеша

Важно понимать, что это не самостоятельный пакет. Он использует имеющийся в MODX класс phpthumb. А тот в свою очередь уже был ранее замечен, как источник проблем с производительностью. Дело в том, что phpthumb перед созданием кеша файла, подсчитывает общий объем кеша, и это процедура совсем не слабая, если файлов довольно много. Чтобы этого избежать, идем в настройки MODX-а и устанавливаем значение 0 для следующих настроек: phpthumb_cache_maxage, phpthumb_cache_maxfiles и phpthumb_cache_maxsize. Тогда phpthumb не будет подсчитывать объем использованного кеша.
Проблема вторая. Низкая производительность.

Сейчас делаю каталог недвижимости, и там на одну страницу выводится 30 карточек. Так вот, на генерацию 30-ти превьюшек уходит 3-4 секунды. Конечно в дальнейшем эти превьюшки уже будут, и повторной генерации не будет, но от этого все равно не легче, так как на сайте более 4000 активных объектов, и будет очень много тормознутых страниц. Да и сниппету каждый раз проверять наличие файла превьюшки — тоже лишняя работа.
В общем, эту проблему я решил исправить радикально — просто заранее сгенерировать превьюшки и сохранить их для документа в отдельное TV-поле. Итак, все по порядку:
1. Создаем TV-шку для превьюхи. С этим все просто и понятно. Единственное — если на сайте будут работать менеджеры, то настройте права доступа на нее, чтобы они даже не видели это поле (это прям в редакторе TV-шки есть вкладка Права доступа).
2. Пишем плагин на сохранение документа. Замысел прост — при сохранении документа, плагин должен проверять значение TV-шки с необрезанной фоткой, и если получает, то выполняет сниппет phpThumbOf и записывает полученный путь до превьюшки в эту специальную TV-шку. Код плагина:
<?php switch ($modx->event->name) { case 'OnDocFormSave': // На всякий случай меняем контекст, если MODX сам не поменял на контекст документа $origin_context = null; if ($modx->context->key == 'mgr') { $origin_context = $modx->context->key; $modx->switchContext('web'); } // Создаем самбнейл превьюшки $TV_source = 2; // Устанавливаете значения своих TV-шек $TV_target = 17; $resource = &$scriptProperties['resource']; if ($photo = $resource->getTVValue($TV_source) and $thumb = $modx->runSnippet('phpthumbof', array( 'input' => $photo, 'options' => 'w=113&h=113&q=100&zc=1', )) and $photo != $thumb ) { $resource->setTVValue($TV_target, $thumb); } // Если контекст меняли, то меняем его на изначальный if ($origin_context) { $modx->switchContext($origin_context); } break; default:; }
Все. Теперь при сохранении документа сразу будет генериться и сохраняться превьюшка. Но что делать, если уже документы есть, и их много? Ведь надо каждый документ пересохранить… Но с этим тоже все довольно просто — не обязательно каждый документ открывать, можно документ пересохранить программно, через API. Лучше всего это делать через Console. Пишем и выполняем вот такой код:
<?php ini_set('max_execution_time', 0); ignore_user_abort(1); $TV_source = 1; // Устанавливаете значения своих TV-шек $TV_target = 2; $q = $modx->newQuery('modResource'); $q->leftJoin('modTemplateVarResource', 'tv', "tv.tmplvarid={$TV_source} AND tv.contentid=modResource.id"); $q->leftJoin('modTemplateVarResource', 'tv2', "tv2.tmplvarid={$TV_target} AND tv2.contentid=modResource.id"); $q->where(array( 'tv.id:!=' => null, 'tv.value:!=' => '', 'tv2.id' => null, 'published' => 1, )); print "Total: " . $modx->getCount('modResource', $q); // Общее число оставшихся документов // return; $q->limit(10); if ($docs = $modx->getCollection('modResource', $q)) { foreach ($docs as $doc) { $modx->error->reset(); $modx->runProcessor('resource/update', $doc->toArray()); } }
Здесь мы получаем документы с картинками, у которых есть картинки, но нет еще превьюшек, и обновляем их через процессор. А в процессоре уже вызывается событие OnDocFormSave и само собой и наш процессор, срабатывающий на это событие.
Вот теперь, после того, как у нас уже есть все превьюшки в специальном TV, нам не надо уже в коде страницы вызывать сниппет phpThumbOf, мы просто вставляем значение этой TV-шки. Нагрузка падает, мы радуемся :-)
UPD: Чуть-чуть дописал плагин. Добавил смену контекста, если текущий контекст — mgr. Дело в том, что для разных контекстов могут быть указаны различные источники файлов, а для mgr этот параметр в TV почему-то не задается, и если источник файлов TV-параметра не базовый (корневой), а контекст mgr, то TV-шка просто вернет неверный результат.
UPD: Добавил еще проверку AND $photo != $thumb. Причина — pThumb, если не смог получить картинку или еще что (ошибка в общем) возвращает оригинальное изображение. Если мы запишем оригинальный путь, то у нас и TV-поле будет заполнено (и мы не будем знать, что для этого ресурса не была создана превьюшка), и значение будет не правильным. Будет потом много гемора.
радикально. у меня тут пара сотен страниц с вызовом phpThumbOf завалялась, а на каждой по 16-30 мегабайтных картинок уже есть и переделывать их реально в ломы, проще сервер купить (что и сделано было)=)
воспользуюсь информацией позже, спасибо.
А на самом деле клева получается. Во-первых, исключается человеческий фактор (не надо учить менеджеров что делать. Один раз прогнал все документы, а потом при любом сохранении документа обновляется превьюха). Во-вторых, идет как чистая статика, сервер вообще не грузится.
Дай я тебя расцелую за это.
Не, вот этого точно делать не надо :-))))
Жесть, слил на 0 настройки, производительность моментально улучшилась. Правильно ли я понимаю что плагин phpThumbOfCacheManager, который устанавливается вместе с самим компонентом phpThumbOf, очищает полностью папку кэша phpThumbOf? Поэтому его лучше выключать?
Правильно ли я понимаю что плагин phpThumbOfCacheManager, который устанавливается вместе с самим компонентом phpThumbOf, очищает полностью папку кэша phpThumbOf? Поэтому его лучше выключать?
Да.
Вот еще на этот компонент посмотри: modx.com/extras/package/phpthumbsup Райн говорит, что он гораздо быстрее работает, чем phpthumbof
кто уже попробовал phpthumbsup? а то я довёл уже до максимума (с помощью Философа) скорость работы phpthumbof, так что у меня он как статика уже работает. кто-то что-то скажет о phpthumbsup?
Я не пробовал еще. Но вот попробуй если что новую альтернативу от Агеля: github.com/AgelxNash/phpthumbon/releases
Охренеть, прорыв прям. Тоже еще не пробовал. Надо анонс замутить для поиска, чтобы всосало в поисковики все три альтернативы.
Все в твоих руках. Протестируй все три решения и напиши обзорный топик.
Пристал. Я не буду писать обзоры. Я лично могу поделиться, но не более.
Вот например: по первым моим тестам обнаружен недостаток в UP и в ON версиях (по крайней мере на этом этапе). Ни одна из них не может брать картинку с внешнего источника. OF может, то попутно обнаружена задержка в нём (проверка наличия кеша), которая составляет 1.5-3ms на одну картинку. Это тоже нехорошо.
Теперь пример, как это развивалось бы, буть это топик: вылезет какой-то умный троль и скажет, что я криворукий, и методом переделки 20 строк кода в плагине и перелопачивании 20 строк ядра всё будет работать, и что я вообще ничего не увидел, поэтому мой обзор — хламищ, мне двойка и я уволен.
Исключительно и только поэтому я никогда ничего не напишу вообще, даже если все будут аплодировать и в руках у меня будет сокровище.
Затроллить и коммент могут.
Вообщем, парился я парился и надоело ON вечно кладёт файл-пустышку в стиле /assets/cache_image/noimage_100x100_31b.jpg, мол оригинал не найден, хотя оригинал есть и доступен.
UP, напротив, заработал полностью, хотя выхов картинки у него совершенно другой, полностью отличается от OF. Например так: /cache/w/100/h/100/src/http://modxclub.ru/uploads/images/00/01/66/2013/07/13/avatar_48x48.jpg
поэтому я пишу тебе, а не им
/cache/w/100/h/100/src/http://modxclub.ru/uploads/images/00/01/66/2013/07/13/avatar_48x48.jpg
жесть!)))
это я вытащил свой аватар с твоего сайта, чтобы для примера не было моего сайта в ссылке. но смысл и расшифровка, думаю, понятны. И гибкость, на мой взгляд, приобретается просто невероятная.
Вот обзор различных аналогов: modxclub.ru/blog/173.html
А вот так я предварительно обновляю все документы, у которых еще нет сгенерированных превьюшек:
<?php ini_set('max_execution_time', 0); ignore_user_abort(true); $modx->switchContext('web'); print '<pre>'; $TV_source = 2; // Устанавливаете значения своих TV-шек $TV_target = 17; $q = $modx->newQuery('modResource'); $q->innerJoin('modTemplateVarResource', 'tvimage', "tvimage.tmplvarid={$TV_source} and modResource.id = tvimage.contentid and tvimage.value != ''"); $q->leftJoin('modTemplateVarResource', 'tvprev', "tvprev.tmplvarid={$TV_target} and modResource.id = tvprev.contentid"); $q->where(array( 'tvprev.id' => null, )); print "<br />Всего документов без превьюшек: ". $modx->getCount('modResource', $q); $q->limit(100); // Создаем самбнейл превьюшки foreach($modx->getCollection('modResource', $q) as $resource){ if($photo = $resource->getTVValue($TV_source) AND $thumb = $modx->runSnippet('phpthumbof', array( 'input' => $photo, 'options' => 'w=113&h=113&q=100&zc=1', )) AND $photo != $thumb ){ $resource->setTVValue( $TV_target, $thumb); // print $thumb; } // print_r($resource->toArray()); }
Скажите пожалуйста, зачем обновлять контекст? И что делать на мультиязычных сайтах?
Скажите пожалуйста, зачем обновлять контекст?
Если вы про это:
        // На всякий случай меняем контекст, если MODX сам не поменял на контекст документа         $origin_context = null;         if($modx->context->key == 'mgr'){             $origin_context = $modx->context->key;             $modx->switchContext('web');         }
то причина следующая: дело в том, что TV-параметры привязываются к источникам файлов (индивидуально для каждого контекста). Но нельзя указать связку TV-поля с конкретным источником файлов в контексте mgr. Если вы не используете отдельного источника файлов. используя по умолчанию filesystem (который от корня сайта работает), то вам бояться нечего. А если у вас в web-контексте другой источник (я часто в основном использую Images (/assets/images/)), то без смены контекста будет использован тот же filesystem и пути побьются, файлы просто не будут найдены.
И что делать на мультиязычных сайтах?
Мультиязычных или мультидоменных? Мультиязычность здесь никакой роли не играет, а если мультидоменный (мультиконтекстный), то используйте во всех контекстах один и тот же источник файлов, что и в web, и никаких проблем не будет.
P.S. на самом деле эта смена контекстов больше имеет значение при выполнении кода в консоли, когда по умолчанию используется текущий контекст mgr, но при редактировании документа через родной редактор страниц, проблем не должно никаких встать.
P.P.S -лучше использовать pThumb вместо phpthumbof, так как первый, в случае если картинка не найдена, использует указанный УРЛ картинки без изменения (то есть просто УРЛ будет на оригинал), в то время как второй или ничего не отдает, или записывается ошибка в TV.
Про источники файлов почитаю, спасибо. Вы может быть не пользуетесь стандартными пакетами, но в наиболее распространённых инструкциях к Babel требуется создание отдельного контекста для языка. По-моему он иначе не работает. И ещё, я может глупость спрошу, но все-таки: у вас в условии if с бухты-барахты появляется переменная $photo. Я как-то привык сперва объявлять переменную, класть в неё что-то, а потом уже в условии проверять. ЗЫ вообще я phpThumbOn установил. но что-то он не больно по скорости обгоняет phpThumbof по моим наблюдениям.
Вы может быть не пользуетесь стандартными пакетами, но в наиболее распространённых инструкциях к Babel требуется создание отдельного контекста для языка.
Я мало использую стандартные пакеты, но во-первых, я знаю хорошо сам MODX (а значит и любой пакет для меня — не проблема), а во-вторых, в свое время не мало пакетов использовал. Вы в данном случае путаете разные вещи. То, что бабел заставляет делать новые контексты, это еще не говорит, что мультиязычность на что-то влияет. Повторюсь: сама мультиязычность ни на что не влияет. А вот по какой причине созданы контексты — это вообще не важно (бабел вас заставил, или сами захотели), здесь есть факт — другие контексты. И вот про эти контексты и их влияние я написал выше — здесь влияние только связка TV-контекст-Источник файлов. Всего написанного выше достаточно.
у вас в условии if с бухты-барахты появляется переменная $photo. Я как-то привык сперва объявлять переменную
У меня написано:
if($photo = $resource->getTVValue($TV_source)
В данном случае не только происходит присвоение, но и ленивое объявление (сам php ее автоматом заводит). Ни логической, ни технической ошибки здесь никакой нет. А ваше желание объявлять переменные в шапке — это ваше право. Я тоже бывает переменные объявляю, но только тогда, когда они в коде расположены далеко друг от друга. Здесь переменная используется в двух строчках кода, близко расположенных друг к другу. Какой смысл их где-то объявлять?
ЗЫ вообще я phpThumbOn установил. но что-то он не больно по скорости обгоняет phpThumbof по моим наблюдениям.
Я не говорил ставить phpThumbOn. Я говорил про pThumb.
Обнаружил такую штуку, что на выходе от сниппета phpthumbof, выходят изображения немного бледные нежели оригинал, в чем может быть проблема? Помогите очень серьезный баг на мог взгляд. Причем если даже размер ставить в настройках один в один с оригиналом, все равно обрабатывает и делает изображение превьюшки более бледной по цвету. Кто сталкивался?
Сергей нет, ставил и с &q=100 и без не помогает
Тогда смотри применяемые фильтры. Наверняка какой-то из них рубит качество.
да нет фильтров то то и оно
{block name=params} {$options = "&q=100&h=360"} {/block} {$image = $object.image|default:$object.imageDefault} {$src = $modx->runSnippet('phpthumbof', [ "input" => $image, "options" => $options ])}
Сталкивались. q=100 решало (100% качество задает). Больше подсказать нечего.
Николай, да не решает в том то и дело ) Сам был очень удивлен
На другом хостинге пробовали?
А хостинг тут причем?
Ну, как бы phpthumb работает через модули php. А вдруг чего не установлено или функции отключены?
как вариант конечно можно попробовать раз уж других нет )
Автору респект!!! В голову не приходило что так можно сделать, нарадоваться не могу.
Но вот не люблю создавать лишний раз тв, пришла идея записывать превью в неиспользуемое поле, например introtext или link_attributes. Сниппету каталога не придется обращаться к тв за картинкой, тем самым еще больше ускорим загрузку. В php я не силен, сделал как смог, поправьте говнокод, буду очень признателен)
$TV_source = 3; $target = 'introtext'; $resource = & $scriptProperties['resource']; $photo = $resource->getTVValue($TV_source); if($photo){ $thumb = $modx->runSnippet('phpthumbon', array( 'input' => $photo, 'options' => 'w=113&h=115&q=100&zc=1', )); $resource->set($target, $thumb); $resource->save(); }
Пожалуйста.
Но вот использовать зарезервированные поля под хранение данных, не для которых они были созданы — это не правильно. Хотите в ресурс сохранять, изучайте эту статью: habrahabr.ru/post/253737/
Спасибо, Fi1osof !!!
Благодаря вам получилась работающая штуковина приведенная внизу, почему-то заработала только с phpThumbOn.
У меня вопрос, можно ли теперь это как-то применить к полю Migx, пытался адаптировать различные решения найденные на различных форумах, но не выходит.

PS про php я знаю только, что он есть.
<?php if ($modx->event->name=="OnDocFormSave") // Плагин срабатывает при сохранении ресурса { $tv = 'image_1'; // ТВ загружаемой картинки $tv2 = 'image_1_preview'; // ТВ создаваемого превью $options = 'w=100&h=100&zc=1'; $tvv = $resource->getTVValue($tv); // Получаем адрес загруженной картинки if (!empty($tvv)) // Если ТВ загружаемой картинки не пустое { $thumbnail = $modx->runSnippet('phpThumbOn', array( 'input' => $tvv, 'options' => 'w=113&h=113&q=70&zc=1', // Параметры сохраняемой превью )); if (!empty($thumbnail)){ $resource->setTVValue($tv2, $thumbnail); // Если phpThumbOn срабтал, то записываем адрес превью в ТВ } } }
Не за что!

Я не очень понял вопрос по MIGX, то есть по сути его вообще не увидел. В чем именно проблема? Предположу, что на сервер MIGX-данные прилетают в JSON и вопрос в том, как выдернуть данные картинок из этой JSON-строки и провернуть операцию.
Но в любом случае, я бы рекомендовал не усложнять это. Лучше все пусть работает в штатном режиме. В производительности выигрыш небольшой, а вот подводных камней многократно прибавится. Потом не будете знать откуда ноги растут.

Добавить комментарий