Как стать автором
Обновить

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

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

Насчёт потенциальной уязвимости, чтобы как-то компенсировать этот момент введены параметры.
$step = 50;
$maxWidth = 2000;
$maxHeight = 2000;
Но если хотите на 100% обезопасить себя от этого случая, то можете просто отказаться от использования контроллеров класса Primage_Proxy_Controller_CopyWithResize ограничившись Primage_Proxy_Controller_CopyById.
Реализована идея, например, в модуле ImageCache для друпала.
Ага, вот только использовать ImageCache можно только на системах построенных на Drupal. К тому же сам ImageCache настолько плох, что адаптировать его под автономное использование представляется очень проблематичным.
НЛО прилетело и опубликовало эту надпись здесь
А зачем переходить на перл?
НЛО прилетело и опубликовало эту надпись здесь
Что мешает использовать тот же ImageMagick напрямую из PHP через shell_exec, например?
НЛО прилетело и опубликовало эту надпись здесь
Можно и без shell_exec, через IMagick, например. И не надо строчку генерировать.
НЛО прилетело и опубликовало эту надпись здесь
и чо? из-за того, что реализация Имеджик на Перле возможно работает стабильнее — переходить на перл? А потом узнаем, что конкатенация строк быстрее на джаве — переходить на джаву? А С# лучше возводит в куб числа между 13 и 58 — переходить на C#?
а почему бы и не выбрать лучшее средство?
Это плохая реализация, есть проще и лучше без ограничений на размеры
Чем плоха реализация Primage?
Какие есть другие «проще и лучше»?
Где вы тут увидели ограничения на размеры?
указывать а пути к картинке ...jpg?hash, где hash это, например md5(соль+имя картинки), а перед ресайзом проверять хеш
При чём тут hash, зачем его проверять перед ресайзом?
Для того, чтобы какой-то негодяй не смог в цикле запросить картинки любых размеров, очевидно.
Ага, и таким образом мы лишаемся возможности статической генерации линков на изображения в виде: domain + URI + id + _small.jpg

Вообще вероятность того, что найдётся злодей, который будет страдать такой фигнёй как генерить вам кучу ненужных картинок настолько мала, что создавать себе столько проблем для исключения подобной ситуации смахивает на параноидальность.
Нет, не лишаемся. Хеш проверяется только при первом создании картинки нужного размера. После этого можно и без хеша обращаться к картинке.
Не согласен. В статике (HTML/CSS/JS) нам неизвестно создана ли трансформированная версия картинки или нет, так что линк на неё мы всегда один и тот же кидаем (т.е. либо всегда с хешем, либо всегда без него).
Так в том то и дело, что «кинуть линк» на создание новой картинки может только ваш сайт, и никто другой (потому что только ваш скрипт знает хеш). В этом и есть назначение хеша.
Я это понял, и говорил о том, что накладные расходы по генерации таких вот «динамических» линков не совсем оправданы с практической точки зрения.

Это может быть будет в следующей версии Primage, где за генерацию линков будет выступать сам Primage_Router в купе с каждым отдельно взятым контроллером. Тогда да, можно будет извращаться с генерирование URL-ов изображений как угодно.
А я вас не понял — с чего вы взяли что вычислить md5() для каждой картинки это долго. Я думаю это раз в 100500 быстрее чем даже быбрать из базы юзеров, для которых нужно вывести аватарки.

Возможно вы имели в виду, что у вас храняться уже сгенерированные в html шаблоны? Тогда и хешы будут сгенерированы, они все равно не изменяются со временем
> с чего вы взяли что вычислить md5() для каждой картинки это долго

А где я писал, что это долго? Это накладно делать каждый раз для генерации линков. Если вводить хеши, то каждый раз для получения линка на ту или иную фотку нужно вызывать php-функцию, а это не всегда удобно.

Вот представьте, есть у вас проект с набором классов моделей, в которых кортинки определены их storage id-шниками. И получается, что если использовать hash, то придётся каждый раз при передаче линка в шаблон оборачивать их вызовом функции, которая будет возвращать URL по id, но это ещё ладно по сравнению с ситуацией, когда у вас проект активно задействует AJAX, как вы тут будете выкручиваться? Откуда вы знаете, на картинку какого разрешения Javascript понадобится линк? Не будете ведь вы прилагать в AJAX ответе к объекту модели к примеру пользователя ещё 50 линков на различные версии его аватарки?

Вот поэтому я и пишу, что использование hash влечёт за собой немало накладных расходов, и я не согласен с тем, что оно того стоит.

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

Но допустим, наш аякс хочет иметь возможность показать любую картинку по запросу пользователя, уменьшенную до 50x50.
Конечно, аякс не знает хеши этих картинок. Но тут можно сделать следующее:
Сделать хеш независимым от имени картинки, а только от размеров. И отдавать этот хеш аяксу заранее.
Тогда и аякс и злоумышленник сможет создать картинки только 50x50.
Ну вот видите, начинаются уже какие-то нелепые ограничения. А всё это чего ради, чтобы минимизировать вероятность того, что вас похацкают с 0.002% до 0.001%?

Если уж человек так боится, что ему нагадят, то пусть использует либо классы картинок (_big, _small, _medium...), либо жесткие классы типа: _25, _100, _300
Спасибо, вы меня натолкнули на мысль о том, что хеш нужно брать только от размеров картинки, без ее имени. Тогда его достаточно один раз отдать в шаблон, а не генерировать для каждой картинки. Все равно, рано или поздно, понадобится превью всех картинок. Это, конечно, работает если превью у нас одинаковые для всех картинок.

А на счет страха — я пишу движок магазина, который юзают многие, а не я один, поэтому и ответственность больше.
хэш тут вообще не нужен.
Возможно есть и другие варианты, расскажите свой
достаточно проверки размеров по списку. формировать список можно автоматически если чо
Я про ограничения
$step = 50;
$maxWidth = 1000;
$maxHeight = 1000;
Ведь могут понадобится размеры не только кратные 50
Ну тут надо либо идти на небольшой «риск», либо отказаться от Primage_Proxy_Controller_CopyWithResize в пользу Primage_Proxy_Controller_CopyById.

Если уж так страшно, что вам кто-то захочет нагадить, то можно проверку на HTTP_REFERRER в .htaccess встроить — думаю 99% злодеев сразу обломятся.
Не обломятся. Добавьте hash и спите спокойно.
Вы накладные расходы учитываете? Это всё равно, что к легковой машине гусельницы прикручивать для случая если вдруг по болоту проехать надо будет.

Оно того не стоит, вам проще будет к психиатру сходить, чтобы он вам от паранои прописал чего-нить.

Расходы на md5() просто смешны по сравнению с ресайзом картинки.
Ох, да кто говорит о расходах на md5()? См. коммент
если тот-самый proxy.php принимает на вход совершенно любые запросы, то да.
а если только допустимые, или именованные (типа 'big', 'small'), то ничего ужасного не произойдет.
Но в таком случае размеры big, small должны определяться не на уровне дизайна, что некошерно.
Ну почему же? Как дизайнер скажет — так и будет. Не дурите людям голову.
Дизайнер должен не сказать, а просто прописать, например если смарти, то как-то так: {thumbnail from=test.jpg width=33 height=75}.
И я не о дизайнере, а о концепции. Размер картинок относится чисто к дизайну, значит и задаваться он должен не в php а в шаблоне
Я не сразу понял, что под дизайном подразумевается вёрстка.

Вообще считаю неправильным использование в вёрстке конструкций типа {thumbnail from=test.jpg width=33 height=75} т.к. это «засоряет» код, ведь везде придётся писать {thumbnail from=test.jpg width=33 height=75} и потом если надо будет что-то подправить, то придётся везде менять.

Кажется разумнее придерживаться концепции классов как в HTML/CSS, ведь аналогия вполне очевидно. Вы ведь в HTML не пишете каждый раз some title bla bla some title… или пишете?
Я не предлагал писать эту конструкцию несколько раз. Наверное мы говорим о разных вещах. Я говорю, например, о таком:

{foreach from=$users item=user}

Имя: {$user->name|escape}
Аватарка:

{/foreach}
ой

{foreach from=$users item=user}

Имя: {$user->name|escape}
Аватарка: <img src='{thubmnail ...}'>

{/foreach}
Есть вероятность, что список юзеров выводится немного в другом виде еще где-то, но с теми же размерами изображений?
Вам именно о таком случае предлагают подумать.
Вряд ли речь шла об этом
Как раз об этом то и шла речь, я потому и писал: «разумнее придерживаться концепции классов как в HTML/CSS». Так или иначе, если верстальщик вставляет где-то линки на изображения конкретных размеров, то велика вероятность того, что подобного же рода линки появятся и в других шаблонах, что в конечно счёте приведёт к дублированию.

Логичнее заранее выделить ряд классов изображений с предустановленными размерами и потом уже ими оперировать не беспокоясь о рисках всякого рода «злоупотребления динамической генерации».
Тогда я совсем запутался. Разве вам не прийдется всё равно писать во всех местах img src=?
Просто одно дело уповать на динамический ресайзинг под каждый частный случай типа 27х27, 29х29… А другое дело ввести ряд классов изображений типа _thumb, _small, _medium, таким образом привязав верстальщика к ограниченному набору вариантов.

У меня сейчас так и есть — small и тп. И получается, что после каждой смены дизайна (выбрали другой шаблон в настройках) приходится в настройках менять размеры превью, так как small для каждого дизайна разный
А это принципиально неправильно — php не должен знать какие размеры нужны шаблону дизайна, и уж тем более выдать другие при ошибочных настройках
Да, я — перфекционист
пусть шаблоны подстраиваются под заранее определённый список размеров. это не проблема
А если бы у вас везде в вёрстке были прописаны конкретные размеры для каждой отдельной взятой картинки, тогда менять пришлось в каждом шаблоне с картинками и это был бы куда более проблематичный процесс.

Я так понимаю, вы слишком жёстко разделяете вёрстку/статику и серверную часть скриптов. Тогда вот вам такой вариант: вынесите конфигурацию proxy.php в виде констант в отдельном скрипте и расположите его в /public_html/css/images_classes.php.
Эта уязвимость закрывается элементарно
Это клёво конечно, но про кэширование уже изменённых изображений разработчик забыл.

А то получится такая ситуация:
заказчик: Сайт сильно тормозит на списке пользователей.
разработчик: OMG
Гоню, сорри =(
НЛО прилетело и опубликовало эту надпись здесь
А не страшно такое делать… Пользователь при желание может устроить DOS путём перебора и запроса урлов картинок:
avatars/{0-999999}_{0-2000}x{0-2000}.jpg

Есть какая либо защита?
См. коммент. Пожалуй добавлю уточнение по этому поводу в топик.
Я бы предложил анализировать запрашиваемые размеры картинок )
Например, 44x78 вряд ли кто-то будет заказывать ) обычно кратные 5 и 2^n.
DOS устраивают не пользователи, а конкуренты :)
Но я лично при реализации похожего механизма беру допустимые размеры из конфига. И еще использую Options +MultiViews в конфигурации Apache, чтобы не приходилось заботиться о разных расширениях картинок (и, соответственно, mime-типах).
Для ресайза лучше все же использовать imagemagick. Он и форматов входящих понимает море и exif почистит, и анимированные гифы разберет, и прозрачные пнг тоже, и палитры из тифов вычистит, и т.д. и т.п. Ну и работать будет шустрее.

Ну и да, в данном случае правильнее все же конвертировать картинки в необходимый формат еще на этапе их загрузки, тогда никакой ДДОС не страшен в разумных пределах (если статику через nginx отдавать). Ну и оригиналы оставить никто не мешает, если вдруг понадобится что-то добавить/изменить.
ImageMagick работает в раз в 5 медленнее, чем GD.
Ссылочку бы на результаты тестирования увидеть.
Протестируйте :)

Из последнего, что видел — например, forum.nginx.org/read.php?21,128545,128741#msg-128741
зато он не вывалится с ошибкой о нехватке памяти (Fatal error: Allowed memory size of X bytes exhausted)
Ну хз может быть не проверял, зато в 5 раз качественней и больше возможностей
Ссылку сейчас не приведу, но сам лично смотрел в top сервера на GD и после перехода с GD на ImageMagick (прямыми вызовами, правда, не через php-модуль) — разница в потреблении CPU была в несколько раз, не в пользу GD. Не важно, кто из них быстрее — важно, кто меньше жрет.
> Не важно, кто из них быстрее — важно, кто меньше жрет.

Ferrari VS Жигули?
Сравнение шикарное, конечно, но, увы, абсолютно некорректное в данном случае.
Я посмотрел ссылку на тест скорости, что вы привели ниже. Разница в полтора-два раза, конечно, великовата, но в случае трафика все равно выиграет то решение, которое потребляет меньше ресурсов.
> в случае трафика все равно выиграет то решение, которое потребляет меньше ресурсов

Не согласен.

Большинство DDOS начинаются не с того, что у системы в какой-то момент использование ресорсов зашкаливает под 100%, а из-за того, что сервер не успевает обрабатывать поступающие запросы и они выстраиваются в бесконечную очередь.

Приведите хоть ссылку на страницу со сравнением потребления ресорсов GD2 и ImageMagic.
От DDOS, увы, не защитит ни первый путь, ни второй. В случае зашкаливания количества запросов или нехватки ресурсов вам в любом случае будет 502-я вылетать. Я бы вообще подобный топик (авторесайз) настраивал на основе модуля nginx. А ссылки такой, увы, под руками нет, самому сделать такой тест сейчас некогда :(
> Я бы вообще подобный топик (авторесайз) настраивал на основе модуля nginx

Я бы тоже, но...
Игорь Сысоев сначала написал image_filter для nginx на imagemagic, но потом переделал на gd. у варианта с imagemagic безбожно текла память. после переписывания на gd процессы nginx стали есть «около 0» CPU, вместо 10%.

пруфлинк: bit.ly/9fKb0c (хабр корежит ссылки с запятой внутри, поэтому bit.ly, но для эстетов — forum.nginx.org/read.php?21,128545,128741#msg-128741 )
> Ну и работать будет шустрее.

Не согласен, см. тест.

В остальном тоже спорно, т.к. «форматов входящих понимает море и exif почистит, и анимированные гифы разберет, и прозрачные пнг тоже» — это можно и про GD сказать.

> Ну и да, в данном случае правильнее все же конвертировать картинки в необходимый формат еще на этапе их загрузки, тогда никакой ДДОС не страшен в разумных пределах

А тут тоже ДДОС не особо страшен, если c Primage_Proxy_Controller_CopyWithResize не слишком баловаться.
анимированные гифы ему очень тяжело даются, к сожалению :(
Зато даются, к счастью :)
Однако не у всех белых людей есть доступ к такой низкоуровневой конфигурации nginx. А ещё ngx_http_image_filter_module не умеет watermarks делать.
Это да, совсем забыл, что не все могут иметь свой nginx :)
А про ватермарки, по моему, это злое-зло их ставить.
по моему, это злое-зло их ставить.

Это решать не вам, а заказчику.
Ну не факт.
Вы же у себя на 1nsk.ru ставите :-) Во всяком случае, когда я пользовался ws, видел ватермарк 1нск.ру в сервисе Граффити.
Обязательно прочтите 20 Image Resizing Pitfalls
Некоторые пункты там конечно относятся только к .Net, но большая часть применима и к Вам.
У нас используется самописный модуль «Пикмен».
Задача его проста — масировать ссылки на внешние источники и предоставлять ресайзы изображений.
Также знать что именно в него загружено(кто, откуда, размеры, тип, формат) — но это так детали.

Для того чтобы получить картинку надо просто сказатать Picmen::invoke($image,$dimmensionName)
на выходе адрес картинки, который сможет на прямую скушать nginx, но если картинки еще нет — отдает пикмену на обработку. И если именно этот ресайз не был недавно запрошен — он не будет выполнен( это к вопросу к захламлению диска ).

Также если картинка долго не использовалась — она подтирается, а если она скачена с внешнего источника — будет изреда проверять его на факт изменения…

В общем в разы круче и умнее описанного тут, только вот один момент сильно различается у них — база данных. Много чего полезного без нее не сделать :(
НЛО прилетело и опубликовало эту надпись здесь
Ага, давайте эти 20 строчек в студию.
Использую похожий подход. На мой взгляд сильно экономится время при загрузке картинки, при этом пользователь, на котором срабатывает ресайз теряет не так уж и много.

Очень важный момент — не забыть почистить кеш для картинки в случае ее изменения.
НЛО прилетело и опубликовало эту надпись здесь
А с видео скрины также снимать будете?
У меня в CMS есть инструкция {image src="%path_to_original_image%"}
она просто выводит картинку как есть, если же указать дополнительные параметры
{image src="%path_to_original_image%" width=«64» height=«64»}
то проверяет есть ли уже отресайзенный файл, если нет, то сама их ресайзит, сохраняет под другим именем формата 64x64_png_%path_to_original_image%
Это в кратце.
Это в смарти, насколько я понял? Тоже хотел так сделать, но тут возникает проблема — все картинки на странице генерируются последовательно одним потоком php — может не хватить 30 секунд на генерацию страницы
Да, смарти. Это уже совсем страшный вариант. 30 секунд хватит на генерацию очень большого числа картинок.
У меня на странице 10 картинок — иногда не хватает, если оригиналы большие. Хотя я тоже так сделал, но неприятно
Всё же — чем хуже, чтобы {image...} делала не ресайз а только правильную ссылку на изображение, а далее как в статье?
Да.
Я спросил чем вариант в статье хуже вашего?
Я разве говорил, что он хуже? Все нормально.
Я просто сам не могу выбрать какой вариант использовать, хотел посоветоваться
Я делаю так. У меня на этапе загрузки аватара человеком аватарка уживаемся до размеров 80х80. Потом уже в разных местах подставляется {image}, который сам же все и ресайзит в потоке страницы без всяких редиректов и 404 ошибок. На сервере 512 оперативы, 1Ггц пентиум и 30 сек на выполнение. Страница с 500 аватарками успевает обработаться за несколько секунд.
А можно линк на страницу? Интересно выдержит ли ваш сервак хотя бы 50 запросов в секунду…
7chances.ru/stat/
Там 50 как раз наберется.
папку с отресайзенными картинками специально полностью почистил.
Вот у вас там какие-то проблемы т.к. у меня 70% аватарок в битом виде показываются. После перезагрузки страницы битых вроде меньше становится, но не на много.
Тогда не страшны нагрузки на один поток
Да и аватарки уже на этапе загрузки ресайзятся до небольших размеров 640х640.
за что минуса собственно?
НЛО прилетело и опубликовало эту надпись здесь
+1, это первое о чем я подумал когда читал пост )))
заказчик: мне еще цветочков на авы надо, чтоб везде разные были, чтоб гифки можно было до 10 мб закачивать, чтобы размер сами пользователи указывали…
разработчик: 0_0
Зачем делать редирект? Не проще ли вывести сontent-type и сделать readfile(); получившейся картинки?
так нагрузка будет больше
Раньше делал примерно также, теперь немного по другому

Пример: i.avamd.info/s/120x80/files/news/8900___jpg____1284225875_13941e59.jpg

/s — статическая папка
/120x80 — разрешение
/files/news/8900___jpg — путь на сервере с оригиналом, где ___ — . (точка)
____1284225875 — дата последней модификации оригинальной картинки (у меня обычно хранится в БД, и она всегда достается, при выборке из БД последних новостей)
_13941e59 — CRC параметров (разрешение + полный путь + дата модификации + секрет) (чтобы избежать генерации бредокартинок)
.jpg — расширение =)

Используется это все на AVA.MD
ImageMagick в отдельный поток, я считаю.
Пару недель назад думал над подобной реализацией, именно из за того, что третий дизайн за три месяца, и трижды меняются размеры изображений.
Офигеть! Хостинг обязательно уронят. Никогда нельзя давать пользователю контроль над генерацией картинок. Пользователю надо давать набор констант типа thumb, large, original и менять их размеры под заказчика.

А ваше решение называется «Здравствуй, DoS!»
Вы не правы. Почитали бы сперва комментарии к топику, не один вы тут такой самый «умный».
я в одном проекте делал генератор заголовков в картинках (долбанный фирменный шрифт заказчика). Очень похоже было, заголовочек генерился если файлика нет, если есть — выпуливался в nginx через X-Accel-Redirect.
Вот для этой задачи подобный подход ну никак не подходит.
С учетом проблемы, о которой уже достаточно написано выше, генерацию подобных заголовков следует полностью переместить в админку, чтоб посетители могли обращаться исключительно к готовым картинкам.
Посетители и так их не генерят, оно втыкается «статичной» картинкой с именем из md5 хеша, скрипт просто находит в DB соответствие заголовку и генерит текст первый раз по нему, потом она так и остается на диске. То есть алгоритм приметрно такой: Есть запись в базе с заголовком, в HTML генерится ссылка на картинку имя которой представляет из себя md5 от заголовка, при запросе картинки скрипт лезет в базу, и только при совпадении — берет тектс из нее, генерит на диск картинку, выпуливает X-Accel-Redirect, если картинка есть — ее nginx отдает сам. Как то так. В принципе возможен DoS, но там полтора инвалида в месяц посетителей
Тогда намного более разумно, чем я предположил. Просто первое что пришло в голову — передача фразы в параметре запроса.

Только вот в запросе к картинке md5 от заголовка. Каким же образом при генерации выявляется нужный заголовок? Перебор md5 от всех заголовков?..
md5 заголовков хранятся в базе рядом с ними, поэтому select title where md5_tile='md5hash' :-)
Хранить решил именно потому, что оно втыкается в шаблон документов, чтоб лишнюю математику при генерации не городить, обновляется только через админку.
Не очень оптимально, но работоспособно. Цели highload'а не было.
Спасибо за терпеливые ответы, несмотря на мое занудство.
Просто решил разложить все по своим местам, чтоб лишний раз на грабли не наступать.
по теме… а в какой структуре вы храните эти файлы?
кто то хранит в виде разбитого идшника /1/1/1/1/1111.jpg
либо /09.2010/%userid%/md5(time).jpg и т.п
кто то в одной папке все складирует

из опыта вашего. как удобнее делать хранилища файлов изображений:?
Так или иначе у каждого изображения свой уникальный id, пусть даже md5(microtime(1)). Далее, если изображений будет > 1000, то делается разделение по папкам.

В случае с Primage, в конструкторе Primage_Proxy_Storage есть параметр $storeSubDirs, который действует следующим образом: если к примеру id фотки d12kdajkh1kjsad и $storeSubDirs=1, то при добавлении фотки в Storage её id станет 3s/d12kdajkh1kjsad, для $storeSubDirs=2 id будет wt/3s/d12kdajkh1kjsad ну и т.д. Сохранение будет происходить в соответствующие поддиректории.

В данном случае id получается не самым красивым (wt/3s/d12kdajkh1kjsad), но мне кажется это всё-таки наиболее оптимальный вариант.
спасибо
Макс, я как бы лично ответил, но многим думаю будет интересно.
Так вот. Количество файлов в директории достаточно серьезно влияет на производительность получения их списка, поэтому лучше хранить в виде /1/1/1/1/1111.jpg. А дело в том, что «This is caused by readdir() returning filenames in a hash-sorted order, so that reads from the inode table would be done in a random order». Самое забавное, что при прямом поиске по имени — падения производительности не происходит, а вот при удалении файлов из такой большой папки, особенно групповом — будет очень плохо
В виде 1/1/1/1 немного избыточно хранить. Если фоток < 100k, то достаточно /ff/32123e21.jpg. Если > 100k && < 2m, то /ff/ff/321312312.jpg, ну и c /ff/ff/ff/321312312.jpg можно сразу в космос…
для 999999.jpg в руте будет 9 папок. 9 папка будет содержать 9 вложений и файл где избыток?
Ага, вот только ничего, что на 1m файлов у вас будет 100k папок? И действительно, где тут избыток… :))))

Предложенный мною вариант, к примеру с /ff/ff/234234.jpg предполагает 256 папок в руте, в сумме ~4k папок. Т.е. при 1m файлов получится ~250 файлов в папке, что вполне приемлимо.
спасибо, не учел =)
Таки да, идея совсем не нова.
Из своего опыта хочу добавить что на некоторых хостингах (у меня на leaderhost) стоит nginx перед апачем и он не передает запросы в апач если запрашивается картинка. Соответственно если ее нет то выпадает 404 и никакой htaccess не спасает.

В качестве быстрого решения пришлось в свое время все картинки как директории прописать, типа: /src/img_tb/200/200/f/some_image.jpg/

В таком случае естественно кеширование путем создания реальной картинки по такому адрессу не рабоатает.
Эх. Жалко, что не прижился у нас Jpeg200 или любой другой аналогичный волновой алгоритм используемый в нем.
З.Ы. Для получения изображения уменьшенного размера в нем нет необходимости считывать и раскодировать весь файл.
чего только не придумают…

на shared хостинге, ну может быть, приходится велосипедировать.

а если сервер свой, да еще хитов в день несколько более чем десять, то…

www.sysoev.ru/nginx/docs/http/ngx_http_image_filter_module.html
потом поверх еще
www.sysoev.ru/nginx/docs/http/ngx_http_proxy_module.html#proxy_cache

P.S. перед тем как минусовать, пожалуйста, подумай!
Да это всё нам известно, кстати уже было в комментах. Вот за то, что перед тем как такое писать комменты не читаете, вас и надо минусовать..)

Вот вы не поверите, но иногда даже имея свой сервер проще запользовать такую вот библиотечку, чем прикручивать и конфигурировать к nginx дополнительный модуль. Кстати говоря он едва ли быстрее чем Primage будет работать т.к. использует ту же самую libgd, тот же принцип работы, НО менее функционален и имеет не такую гибкую возможность конфигурации.

Уточню: ngx_http_image_filter_module будет конечно быстрее работать, но не намного, думаю на 5-10% т.к. в данном случае 90% процессорного времени и ресурсов так или иначе будет приходиться на libgd.
не надо.
там ничего не было написано про кэширование, удивительно, как автора того комментария только за это не забросали ссаными тряпками любители php, у которых 5% туда, 10% сюда — пофиг, «зато проще!» :)

«прикручивать» — в портах FreeBSD, например, он включается галкой при установке, ничего прикручивать не нужно.
«конфигурировать» — Ваш скрипт насчитывает 39 строк. конфиг соответствующего location на сервере врядли будет превышать 5.

единственное, действительно, watermark не положишь поверх. но и то только out-of-box. если бы заказчик настаивал с требованиями [так уж вышло, что я обычно сам себе заказчик :) ], то подпилить image_filter наверное заняло бы те же самые 40 строк, только на Си :)
> любители php, у которых 5% туда, 10% сюда — пофиг, «зато проще!»

И вы с этим не согласны? О чём с вами можно после этого разговаривать? :)

> Ваш скрипт насчитывает 39 строк. конфиг соответствующего location на сервере врядли будет превышать 5

Да ладно :) Ну ка давайте эти 5 строк, в студию!
> И вы с этим не согласны? О чём с вами можно после этого разговаривать? :)
я админ, мне эти 10% на загруженных серверах потом боком выходят :) тем программистам, у которых нет действительно веских причин (тот же watermark), со мной действительно не о чем разговаривать :) я же не вьедливости ради, надо так надо — можно и написать. просто большое начинается с малого. вон в комментариях выше уже отметились «любители проще» со своими «readfile() + content-type», для которых x-accel-redirect 100 в гору, что будет «слишком сложным» :)

> Да ладно :) Ну ка давайте эти 5 строк, в студию!

примерно такой случай. где-то у нас есть сервер, отдающий аватарки исходного размера по урлу типа h_t_t_p://server/users/username/avatar, в моем случае это система центральной авторизации и картинки 150*150.

в нужном месте (в моем случае это чат, которому нужны две аватары, 30х30 и 80х80) пишется следующий location, обслуживающий урлы вида /avatar/две_цифры/username. например /avatar/30/cadmi сходит, возьмет картинку 150х150, сресайзит до 30х30, скэширует и отдаст клиенту. /avatar/40/cadmi сресайзит на 40х40

прошу прощения, тэги в моих комментариях режутся (заминусовали карму за «наезд» на гугль), поэтому немного каша.

это без кэша

location ~ ^\/avatar\/(\d{2})\/(.*)$ {
proxy_pass h_t_t_p://server/users/$2/avatar;
image_filter resize $1 $1;
}

это с кэшем

proxy_cache_path /where/is/cache/avatars levels=1:2 keys_zone=avatars;

location ~ ^\/avatar\/(\d{2})\/(.*)$ {
proxy_pass h_t_t_p://server/users/$2/avatar;
image_filter resize $1 $1;
proxy_cache avatars;
}

предупреждаю: желающим скопипастить конфиг к себе я оставил домашнее задание :) дабы копируя, хоть немного почитали документацию nginx, вдруг по дороге узнают для себя еще чего-нибудь полезного. впрочем в части, касающейся непосредственно обсуждаемого вопроса, конфиг абсолютно рабочий.
Это всё конечно хорошо, но если уж говорить о «Ваш скрипт насчитывает 39 строк. конфиг соответствующего location на сервере врядли будет превышать 5» то вы как-то не объективно сравниваете.

Для приведённого вами примера конфирационная часть proxy.php выглядела бы так:

$avatarsStorage = new Primage_Proxy_Storage('data/avatars', 'jpg', 90);
$avatarsProxyStorage = new Primage_Proxy_Storage('public_html/img/avatars', 'jpg', 80);
$avatarsDynamic = new Primage_Proxy_Controller_CopyWithResize($avatarsStorage, $avatarsProxyStorage);
$router->addController('avatars/{id}_{width}x{height}.jpg', $avatarsDynamic);

Ну т.е. те же 5 строк, НО

1. Ваш скрипт хоть и реализует генерацию динамических аватарок, но рискует положить сервер если кто-нибудь запустит на нём перебор

2. Что будете делать, если количество аватарок зашкалит за 10k? Не боитесь, что начнутся тормоза в связи с таким большим количеством файлов в папках? Primage в этом случае может быть настроен на автоматическое создание под-папок

Вообще это я уже конечно цепляюсь. Решение с модулем nginx очень даже классное, учитывая его возможность ставить обработку изображений в очередь, добавлю это в Primage.

Спасибо, что так подробно всё описали.
> 1. Ваш скрипт хоть и реализует генерацию динамических аватарок, но рискует положить сервер если кто-нибудь запустит на нём перебор.

перебор, простите, чего? Вы что-то недопоняли, очевидно. объясните, что имеется в виду, и что из этого нельзя перебрать в primage. я либо соглашусь, либо объясню непонятное :)

> 2. Что будете делать, если количество аватарок зашкалит за 10k? Не боитесь, что начнутся тормоза в связи с таким большим количеством файлов в папках? Primage в этом случае может быть настроен на автоматическое создание под-папок

внимательно посмотрите на строчку proxy_cache_path /where/is/cache/avatars levels=1:2 keys_zone=avatars;
levels=1:2 это для Вас :) двукратная вложенность. ключом и именем файла в кэше является результат функции md5 от проксированного URL. параметр levels задаёт уровни иерархии кэша. в моем примере, /avatar/30/cadmi имя файла в кэше будет примерно такого вида: 7950e8e3e6061d6fa113c4edb71b010b (md5 от /avatar/30/cadmi)

лежать будет в /where/is/cache/avatars/b/10/7950e8e3e6061d6fa113c4edb71b010b. жаль, тэги мне недоступны, выделил бы bold'ом. b — последний символ, 10 — два предпоследних. это и есть levels=1:2 в proxy_cache_path. сделаете levels=1:2:2 будет три уровня вложенности и путь типа /where/is/cache/avatars/b/10/b0/7950e8e3e6061d6fa113c4edb71b010b

подробнее на www.sysoev.ru/nginx/docs/http/ngx_http_proxy_module.html#proxy_cache_path

спрашивать про ограничение на максимальный размер кэша (количество и суммарный размер файлов), а также кто за ним следит и кто его чистит, я не буду :) потому что в сорцы primage не смотрел, возможно там это есть.
> перебор, простите, чего?

Имелочь ввиду перебор по
/avatar/30/cadmi
/avatar/31/cadmi
/avatar/32/cadmi

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

ngx_http_proxy_module — отличный модуль!
Почитаю о нём ещё подробнее, думаю найду много чего, что полезно будет в Primage реализовать.

Спасибо, что просвятили :)
а? перебор размеров? так это чтобы регэксп не захламлять, там было написано просто \d{2} про «две цифры».
в реальной жизни можно написать регэксп, который пропустит только то, что реально нужно.

ну, например…

location ~ ^\/avatar\/(30|40|60)\/(.*)$ {
бубубубу
}

… и пропустит только /30/ или /40/ или /60/ а 31 и 32 обломятся и будет 404.
а хеш функция от закрытого ключа в данном случае как раз ни при чем. соли то там нет — просто имя файла в кэше вычисляется из URI. если скормить /31/ то конечно все поедет и будет ресайзиться. но сразу писать перечисления было не педагогично. это ж уже задача на man pсre и на man pcretest, а не про nginx :)

для линка с использованием хеш функции от ключа есть другой модуль, ngx_http_secure_link
www.sysoev.ru/nginx/docs/http/ngx_http_secure_link_module.html

не надо.
там ничего не было написано про кэширование, удивительно, как автора того комментария только за это не забросали ссаными тряпками любители php, у которых 5% туда, 10% сюда — пофиг, «зато проще!» :)

«прикручивать» — в портах FreeBSD, например, он включается галкой при установке, ничего прикручивать не нужно.
«конфигурировать» — Ваш скрипт насчитывает 39 строк. конфиг соответствующего location на сервере врядли будет превышать 5.

единственное, действительно, watermark не положишь поверх. но и то только out-of-box. если бы заказчик настаивал с требованиями [так уж вышло, что я обычно сам себе заказчик :) ], то подпилить image_filter наверное заняло бы те же самые 40 строк, только на Си :)
> любители php, у которых 5% туда, 10% сюда — пофиг, «зато проще!»

И вы с этим не согласны? О чём с вами можно после этого разговаривать? :)

> Ваш скрипт насчитывает 39 строк. конфиг соответствующего location на сервере врядли будет превышать 5

Да ладно :) Ну ка давайте эти 5 строк, в студию!
если это делать на продакшене, то после первого же апдейта размеров сайт ляжет, ибо все юзеры ломанутся конвертировать изображения
Если посещаемость > 1m хитов, то возможно. В противном случае большинство shared-хостингов вполне потянут разовую генерацию по 200-300 изображений в минуту.

Но вообще проблемность ситуации имеет место быть, думаю добавить плагин некой «очереди» в Primage, который будет ограничивать колличество генерируемых картинок в один момент времени.
на 1 хит приходится десятки превьюшек. итого — 10 пользователей кладут сервер на лопатки
потратив еще две строчки, можно к приведенному выше конфигу добавить соответствующие limit_req_zone и limit_req с нужным burst: количество в секунду, остальное в очередь, если не успели, то ошибку. а добавив еще одну строчку, вместо ошибки выдавать некую дефолтную картинку.

слова «ну-ка» и «давайте в студию» по некоторым правилам хорошего тона, кстати, подразумевали бы какой-то комментарий, а не смущенное молчание :)
А вот :)
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за авторитетное мнение… насмешили :) Вот странно, что из ~160 комментов к топику, только вы догадались такой нелепый вопрос задать, наверное потому что самая умная :))))

Ну да ладно, раз уж спросили — отвечу. Для того чтобы ресайзнуть картинку можете использовать один только класс Primage, см. пример.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации