Пользователь
0,0
рейтинг
8 августа 2013 в 22:53

Разработка → Простое создание миниатюр и хранение их на Amazon S3

Ни один сайт не может обойтись без генерации миниатюр изображений. В интернете можно найти миллион статей на эту тему. Возможно, кому-то пригодится и это решение.

Требования:
  • Изображения хранятся на удаленных серверах. У нас есть только ссылки на эти изображения
  • Миниатюра должна формироваться любого заданного размера в момент непосредственного обращения к ней
  • Должна быть предусмотрена защита от вредителей
  • Миниатюра должна храниться на Amazon S3 и быть доступна по поддомену основного сайта. Количество бакетов на S3 и соответственно поддоменов неограничено


Реализация:
Допустим, что имя нашего сайта domain.com

1) На Amazon S3 создаем 4 бакета с названиями ic1.domain.com, ic2.domain.com, ic3.domain.com, ic4.domain.com (названия поддоменов могут быть разными, в нашем случае – это сокращение от image cache)

2) Заходим в настройки каждого бакета и ставим галочку напротив Enable website hosting

3) В поле Edit Redirection Rules прописываем правила для редиректов с несуществующих страниц:

<RoutingRules>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>domain.com</HostName>
            <ReplaceKeyPrefixWith>s3/thumbs/</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>403</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>domain.com</HostName>
            <ReplaceKeyPrefixWith>s3/thumbs/</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>


4) Заходим в панель управления доменным именем domain.com и создаем поддомены, аналогичные названиям бакетов. В CNAME прописываем Endpoint из настроек бакета.

Что мы только что сделали:
Создали 4 поддомена, на которых будут храниться миниатюры. Каждая миниатюра будет иметь адрес типа: ic1.domain.com/miniatura.jpg

Как только к этому файлу будет первое обращение, Amazon увидит, что такого файла в бакете ic1.domain.com нет и перенаправит пользователя на указанный Вами путь, который конкатенируется из HostName + ReplaceKeyPrefixWith: domain.com/s3/thumb/miniatura.jpg

На сервере отлавливаем через роутер или .htaccess файл запрос, создаем миниатюру переданного файла, отправляем ее на Amazon S3 в нужный бакет и отображаем миниатюру первому пользователю.

Остальные пользователи – будут получать ее непосредственно с Amazon S3.

Поехали дальше.
До сих пор непонятно как именно передавать параметры для минимизации и ссылку на саму картинку.
Что имеем:

Разумеется, что ссылка вида: ic1.domain.com/?Img_url=http://blablabla.com/photo15.jpg&Thumb_w=100&Thumb_h=100 нас абсолютно не устраивает, т.к. во-первых Amazon нас не поймет, а во-вторых – нет никакой защиты от вредителей.

Вариантов, как создавать такие ссылки может быть миллион. Мы решили обойтись без промежуточных таблиц или записей и посмотрели в сторону RC4 криптования. Звучит страшно. Но давайте посмотрим, что получилось:
ic2.domain.com/e2/PUuxR1p~D~Jgl5PrnPMLh4OA0sO899rjZgzgWFU_.jpg
Довольно таки милая ссылка. По крайней мере не намного страшнее чем md5 хеш. Зато намного информативнее. Если расшифровать крипт, получим:
blablabla.com/photo15:100@100
То что нам надо – все данные по фотографии, размеры и плюс ко всему, не зная пароля от крипта – никто не сможет нагенерить «лишних миниатюр»

Вопрос остался за малым – реализовать функцию, которая будет принимать ссылку на изображение, размеры и возвращать ссылку на миниатюру (ну и разумеется все остальные скрипты).

По ссылке на github некоторая реализация поставленной задачи: github.com/wolflingorg/s3thumb

Структура:
CS3Thumb.php – основной класс
S3.php – класс для работы с Amazon S3
CRC4Crypt.php – класс для криптования в RC4
CThumb.php – класс для создания миниатюр

Как использовать:
$Thumb = new CS3Thumb($backets, $accessKey, $secretKey, $cryptpsw = 'password');

Где:
$backets — массив с наименованиями бакетов (они же поддомены)
$accessKey и $secretKey – доступы к амазону
$cryptpsw – пароль для криптования ссылок

Для того, чтобы получить ссылку на изображение, используем:
$Thumb -> url("http://blablabla.com/photo15.jpg", 100, 100);

Для того чтобы создать миниатюру, переместить ее на S3 и отобразить первому пользователю:
$Thumb -> process("e2/PUuxR1p~D~Jgl5PrnPMLh4OA0sO899rjZgzgWFU_.jpg");

, где e2/PUuxR1p~D~Jgl5PrnPMLh4OA0sO899rjZgzgWFU_.jpg — то, что будет в ссылке после domain.com/s3/thumbs/(*), после редиректа с Amazon

Надеюсь, статья будет полезна. Спасибо.
Максим Оданец @wolfling
карма
5,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (16)

  • +3
    4) Заходим в панель управления доменным именем domain.com и создаем поддомены, аналогичные названиям бакетов. В IN A записи прописываем Endpoint из настроек бакета.


    Не уверен, что такое IN A на domain.com, но видимо это A record. В таком случае endpoint надо прописывать не в A но в CNAME.
    • 0
      Спасибо. Действительно «поспешил». Разумеется CNAME. Исправил.
  • 0
    > класс для криптования в RC4
    Как же это небыстро, наверное работает. Плюс, имя нечитаемое. Чем вам не угодил вариант photo15_100x100.fd34ab89.jpg, где fd34ab89 — md5 от photo15_100x100 и пароля?

    Еще не понял, зачем понадобилось 4 бакета.
    • +1
      4 бакета для балансировки. чтобы параллельно загружать в браузере
      • +1
        Все верно. Браузер одновременно с одного домена загружает не более чем в 4 потока (могу ошибиться с числом). Для того, чтобы оптимизировать скорость загрузки страницы в целом — статический контент мы вынесли на отдельные поддомены. И чем больше их будет, тем быстрее страница загрузится. Нам для миниатюр хватило 4.
        • 0
          Cейчас их в среднем по больничке 6 по умолчанию (4 было этак лет 5 назад).
    • +1
      Спасибо за вопрос. Из описания действительно не очень понятно, что сами изображения «не наши». Речь идет о «каталоге товаров» в который компании могут загружать свои xml прайс листы, в которых в свою очередь есть ссылки на изображения товаров на «их» сайтах (сайтах компаний).
      Ссылки там настолько разнообразные, что простым photo15_100x100 у нас не получилось «отделаться». Пришлось изобретать велосипед, для того.

      Что касается скорости работы RC4 и md5 — скажу честно — не замеряли.
  • 0
    Я без s3, но с подобным алгоритмом делал.
    Пользователь обращался к nginx по адресу: /thumb/2013/08/120/160/12345.jpg
    Nginx проверял существование файла, и если не находил — перенаправлял запрос в php скрипт.
    Дабы папки не содержали по 100500 файлов, делил по месяцам.
    /миниатюры/год/месяц/ширина/высота/id.jpg. Скрипт отображал клиенту миниатюру и клал сгенерированный результат по нужному пути. Следующий клиент смотрел миниатюру уже без участия php.
    Это я все к чему, я не понимаю, зачем использовать криптовые ссылки?
    • +1
      обычно используют не криптованные ссылки, а подписанные. Смысл в том, чтобы защититься от такой зловредной атаки — мы делаем универсальный скрипт для тумб, и он может делать тумбы любого размера. Поэтому враг может генерировать много запросов для картинок разного размера, и таким образом забить дисковое пространство. В отдельных случаях и процессорные мощности играют роль, ибо ресайзинг требует проц, но главное таки место.
      Если ссылка подписанная, то мы можем быть уверены, что сгенерированна она именно нашим скриптом.
      Автор использовал именно шифрование потому, что в его приложении не было никакого смысла сохранять хоть что-то от исходного названия — они у него непригодны для жизни.
      ИМХО шифрование должно быть тяжеловатым, но это только теория.
      • 0
        А может отресайзить 1 раз все нужные размеры?
        • 0
          Можно и даже нужно. И желательно в фоне.
          Но, когда добавляется еще один «тип миниатюры», а в проекте уже имеется довольно большое количество исходных изображений, то проще клиенту отдавать динамику по запросу. И, конечно же, никто не запрещает фоном запустить процесс прегенерации миниатюры для других изображений.

          Т.е. речь идет об использовании обоих подходов: генерация по запросу + прегенерация при загрузке или добавлении новых «миниатюр».
          • 0
            Как по мне — все зависит от ситуации.
            Как я уже говорил, в нашем случае — картинки находятся на сторонних сайтах. Мне не кажется логичным «вытягивать» их все т.к. большинство из них вообще могут и не отобразиться в на сайте во многих размерах.
      • 0
        Возьмем к примеру товар мобильный телефон. В нашей БД к нему есть N ссылок на изображения:

        h t t p: / / fptshop.com.vn/Content/Images/uploaded/Phone%20IMG/SONY/Sony%20Xperia%20V%20LT25i%20d.jpg
        h t t p: / / w w w .mobile-review.com/review/image/sony/xperia-v-fl/01.jpg
        h t t p: / / i1-news.softpedia-static.com/images/news2/IFA-2012-Sony-Xperia-V-Hands-on-6.jpg?1346338399
        h t t p s: / / encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcTl0rA49eQb3TgPzec7qPcGUPqPoa4Y5aTMsswD2nPTK1eTbVr8

        Ко всем из них нужно сделать миниатюры. Разумеется крипт для формирования ссылки не самый красивый вариант. Нас он устроил.
      • 0
        Не забывайте, если запрос попадает в скрипт, мы получаем «безграничные» возможности для работы с изображением, в том числе, мы можем сделать валидацию. Например ограничить варианты размеров, проверить реферала и принадлежность миниатюры к странице etc. Как по мне, так это вполне себе надежно и защитит от атаки. Конечно, все может быть сложно и валидацию не всегда можно сделать.
  • 0
    Вы бы еще HttpRedirectCode указали в конфиге. А то амазон по умолчанию отправляет 301.
    Так как я не возвращал картинку из своего кода, а делал обратный редирект на амазон — зацикливание встретило меня.
  • 0
    Спасибо! А Вы планируете класс S3 переписать под работу c их новым шифрованием AWS4-HMAC-SHA256?

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