Модуль ImageResizer для IIS


По следам недавней статьи об изменении размеров изображений на сервере, я решил поделиться опытом внедрения модуля ImageResizer для IIS. Конечно написать простой обзор было бы слишком скучно, но мы не ограничились простым внедрением модуля.
Итак, у нас в распоряжении был купленный ImageResizer + набор плагинов Cloud Bundle для работы с облаками.

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

Статьи хранятся в БД, редактор может прикрепить к статье заглавную картинку. Изображение хранится в Amazon S3. Как Вы понимаете, редактор может загрузить изображение как маленькое, так и огромное и при этом нельзя терять оригинал.

Видео файлы загружаются, обрабатываются и хранятся в облачном хранилище видео BrightCove. После загрузки видео в хранилище, оно всячески обрабатывается, перекодируется в несколько потоков разного формата и для видео автоматически выбирается миниатюра (thumbnail).
Естественно, раз все это происходит в облаке, то и картинка хранится в облаке, а мы получаем прямую ссылку для вставки. Но и тут картинка не всегда может соответствовать требуемому размеру.

Итого мы имеем набор изображений в Amazon S3, набор изображений в BrightCove и все изображения нужно уметь быстро отдавать уже подогнанными под нужный размер. Кроме того, в разных частях сайта могут использоваться различные размеры изображений. При этом набора плагинов для кеширования у нас не было, зато у нас был CloudFront.



Как ни странно, товарищ bitmap в своей статье не упомянул, как именно он изменяет размер изображения, ибо есть множество способов (муки выяснения необходимого варианта с заказчиком Вы можете лицезреть на фото). Можно растягивать в рамку, получая растянутые лица, можно сохранять пропорции и заполнять освободившееся пространство цветом. Можно сохранять пропорции и обрезать края. Модуль также позволяет еще кучу не нужных нам рюшечек, типа наложения фильтров и прочего. В итоге, на одном проекте мы стали растягивать аналогично растягиванию css, а на другом проекте заполнение цветом.

Стандартный сценарий


По умолчанию из коробки предполагается что изображения и IIS расположены на одном сервере и изменение размеров осуществляется простым добавлением параметров к ссылке:
/image.jpg?height=200&width=300
или
/image.jpg?h=200&w=300.
Отлично, но как забрать изображения из Amazon S3?

S3 Reader


Тут нам на помощь приходит первый из набора плагинов Cloud Bundle: S3 Reader. Путем нехитрых настроек web.config, он позволяет использовать ссылку следующего формата:
/s3/bucket/folder/image.jpg?h=200&w=300

RemoteReader plugin


Ок, теперь мы можем отдавать картинки из S3, но S3 это довольно популярная вещь, как насчет совершенно стороннего сервера? Здесь включается RemoteReader plugin. Тут уже не все так просто. Конечная ссылка имеет следующий формат:
/remote.jpg.ashx?w=200&height=300&urlb64=45b45c4a2099b...&hmac=a2099ba2099b.

Где remote.jpg.ashx это конечная точка для перехвата ссылки модулем, параметр urlb64 — это нехитро зашифрованная при помощи HMAC SHA-256 конечная ссылка на удаленный сервер с картинкой. Данную ссылку генерирует специальный метод:
RemoteReaderPlugin.Current.CreateSignedUrl("http://remote.com/image.jpg");

CloudFront


Ок, с основной задачей мы справились. Теперь нужно как-то кешировать результат, иначе быстро все это работать не будет.
Кеширующих плагинов у нас нет, зато есть такая волшебная вещь, как CloudFront custom origin. Мы создали новую дистрибуцию CloudFront, указывающую на наш домен. Так что теперь у нас есть ссылки формата:
httр://my-distribution-name.cloudfront.com/remote.jpg.ashx?w=200&height=300&urlb64=45b45c4a2099b...&hmac=a2099ba2099b
и
httр://my-distribution-name.cloudfront.com/s3/bucket/folder/image.jpg?h=200&w=300

Но теперь наш сайт стал открываться по адресу httр://my-distribution-name.cloudfront.com, забивая cloudfront. Тогда мы быстро добавили раздел для картинок. И CloudFront со ссылки
httр://my-distribution-name.cloudfront.com/s3/bucket/folder/image.jpg?h=200&w=300
cтал перенаправлять запрос на
httр://my-site.com/images/s3/bucket/folder/image.jpg?h=200&w=300

Вроде бы вся должно работать, но внезапно выяснилось, что CloudFront нещадно рубит длинные строки параметров и мы получили тонны ошибок и не открывающихся картинок. Тут нам помог CloudFront plugin, позволивший преобразовать ссылки к следующему формату:
httр://my-distribution-name.cloudfront.com/remote.jpg.ashx;w=200;height=300;urlb64=45b45c4a2099b...;hmac=a2099ba2099b
и
httр://my-distribution-name.cloudfront.com/s3/bucket/folder/image.jpg;h=200;w=300

Короткие ссылки


Все это отлично работало, но инженерная мысль пошла еще дальше. Что если кто-то начнет генерировать не нужные нам разрешения картинок? Максимальный размер увеличения конечно ограничен, но все равно так можно заDDOSить сайт, да еще и полученные картинки разнесутся по сотням серверов CloudFront по всему миру. Ну и конечно длина ссылки просто ужасна. Тогда мы решили реализовать функционал коротких ссылок.

При генерации ссылки мы складываем в базу полученную длинную ссылку вида:
/remote.jpg.ashx;w=200;height=300;urlb64=45b45c4a2099b...;hmac=a2099ba2099b
и получаем её id в таблице. В вёрстку же выплевываем ссылку формата:
httр://my-distribution-name.cloudfront.com/42

CloudFront обращается к нам по адресу:
httр://my-site.com/i/42

Мы перехватываем запрос, выбираем длинную ссылку по ключу 42 из БД, в серверном коде делаем HttpRequest к самому себе, чтобы его перехватил ImageResizer и в ответ выдаем результат его работы. CloudFront складывает полученную картинку на своих серверах и больше мы не получаем этот запрос до следующей инвалидации кеша.

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

В итоге конечная для пользователя ссылка имеет вид:
httр://short.com/42

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

Заключение


В заключение хочу сказать, что плагин прекрасно работает, имеет кучу различных настроек и плагинов на все случаи жизни, имеет режим отладки на случай непредвиденных ситуаций. Автор отвечает при обращении по проблемам и даже вроде бы платит за найденные баги. Конечно, модуль платный, но он того стоит.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 5

    0
    Хранить в БД линки на себя же… а потом делать скрытые селф-реквесты — да вы знаете толк в извращениях. Честно говоря, ожидал увидеть в статье решение с клиентским ресайзом картинки при аплоаде, интегрированным с ASP.NET.
      0
      Да, если бы это все не кэшировалось в CloudFront, то конечно лучше было бы конвертировать при загрузке в несколько разрешений, но тут есть свои минусы — если добавится новая страница с новым разрешением, то придется ресайзить огромное количество картинок, а тут все происходит на лету и очень редко.
        0
        Просто ресайзить «на лету» можно на уровне кода (контроллера в случае MVC), с кешированием и прочими плюшками, без создания отдельного реквеста и хранения кусков URL в базе. Причина в том, на сколько я понял, что ImageResizer предлагает универсальное решение для «домохозяек» со своим хендлером и настройками ресайза в строке запроса — с самого начала понятно, что это не безопасно, зато очень просто и удобно в использовании. Но не профессионально и не эффективно. Каждый запрос к картинке умножается на 2 абсолютно лишних телодвижений.
          0
          ImageResizer из коробки не требует никаких лишних телодвижений. При использовании плагинов семейства Performance все будет работать довольно достойно. В нашем же случае лишние телодвижения оправдывают себя, так как в общем-то каждая картинка ресайзится ровно один раз при первом запросе в CloudFront. Конечно можно было написать свой велосипед для изменения размеров на уровне контроллера, но опять же в нашем случае встает вопрос хранения кэша. Для хранения изображений мы все равно используем S3 и CF. Мы не храним пользовательский контент на своих серверах, а отправка картинки в S3 довольно затратная операция, к тому же все равно на выходе мы подсунем пользователю CF- ссылку, так что в нашем случае считаю использование данного решения для домохозяек вполне обоснованным.
            0
            В любом случае отправку изображений на S3 не избежать :) Хотя да, если все хранить снаружи, как у вас, большого прироста производительности со своей велосипедной реализацией, видимо не получится.

    Only users with full accounts can post comments. Log in, please.