IIS — изменяем размер картинок на лету

Почти в каждом веб-проекте мы сталкиваемся с задачей показывать те или иные изображения в разных размерах. Всё просто — изображение должно показываться в размере, требуемом контекстом. Если вы разрабатываете каталог с разными представлениями, то таких контекстов может быть много. А возможно, что потребуется сделать размер картинки адаптивным по отношению к размеру окна браузера (например, так делают Picasa Web Albums).

Я расскажу о способе решить эту проблему раз и навсегда.

Что мы хотим


В простейшем случае мы хотим использовать URL картинок в таком виде:
<img src=/img/photo.jpg@100x100”/>

Это должно отображать картинку размером 100 x 100.

Реализация


Самый прямой способ решить эту проблему — модуль для ASP.NET, который будет на лету изменять размеры картинки и кешировать результат. Сразу отмечу, что естественно я не первый, кто озаботился этой проблемой. Но решение, которое предлагаю я, на мой взгляд проще в использовании, понимании и модификации.

Код


Итак, чтобы всё работало, нам понадобится собственно сам класс модуля и одна строчка в Web.config.

Модуль хоть и невелик, но выкрадывать сюда простыню кода смысла тоже большого нет.
Поэтому я создал gist c кодом. Скопируйте этот код себе в проект (например в App_Code).

Теперь короткие настройки в вашем Web.config. Добавьте в тег <modules> или <httpModules> (последний, если вы используете ASP.NET Devopment Server) такую строчку:
<add name="ImageResizeModule" type="GarageTools.ImageResizeModule" />

Всё готово.

Используем


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

Но есть ещё несколько модификаторов, которые помогут в разных ситуациях. В конец URL можно дописать такие буквы-модификаторы:

  • s — модуль не будет увеличивать картинку, если она меньше требуемых размеров.
  • c — модуль будет следить чтобы размер результирующей картинки был строго требуемого размера — если необходимо, он добавит белые поля по горизонтали или вертикали.
  • p — модуль вернёт PNG вместо JPEG.

Например вот так: /img/aspnet.png@150x100sc.

Тонкие настройки и модификация


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

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

Приятного использования.
Поделиться публикацией

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

    +2
    А не проводили тесты по скорости отдачи просто статического файла и с ресайзом через Ваш модуль с кешированием и без?
      +1
      Тестировал ab.
      В 1 — 10 потоков, разницу между отдачей модулем кешированого файла и когда модуль целиком убран (runAllManagedModulesForAllRequests=«false»).
      По результатам получается разница в пределах 20%.
      +1
      Боюсь, что даже один пользователь, который в несколько потоков начнет запрашивать несколько картинок c разницей в размерах 1px, сможет, во-первых, «убить» кеш, а во-вторых, занять практически все процессорное время.
        +1
        Думаю, что можно доработать и указывать модулю список допустимых разрешений.
          0
          Не вариант. Вы же не будете указывать список допустимых разрешений для каждой картинки? Простого ограничения на размер сверху тоже будет недостаточно.
            0
            Ну если для разных картинок размеры могут быть совершенно разными, то да, Вы правы. Но если брать, например, каталог продукции, то в большинстве случаев для всех картинок каталога список нужных размеров будет одинаков. Хотя в этом случае иногда проще сделать ресайз при загрузке, если конечно не планируется частое изменения размеров.

            Вообщем, не панацея конечно, но в некоторых ситуациях модуль будет удобен для использования, имхо.
          0
          Это так. Но эта проблема относится не только к ресайзу картинок. Примеров тяжёлых серверных сценариев много.
          Собственно бороться с флудом надо уже изученным способом — определять источник и банить.

          Но в простейшем случае я бы действительно опделели готовый список профилей для ресайза на сайте да и всё.
          Другое дело ресайз адаптивный (Picasa Web) — тут только бороться с флудом как таковым.
          +2
          Рекомендую взглянуть на проект imageresizing.net, имеет много различных опций и плагинов.
          Сам пока не использовал, но как утверждает автор, библиотека используется на довольно крупных сайтах с хранилищем картинок 10Тб — 20Тб.

          Пост Скотта Хансельмана об этом.
            0
            Замечательный проект.
            Но настолько умный, что 20% overhead-а не отделаешься.
            По ab у меня не получилось меньше 40%. Хотя, может сейчас что-то поменялось у них.
              0
              Провел тесты — без использования кеширования оверхед состалял где-то 40-50% (делался ресайз, кроппинг с параметром scale=canvas); однако при использования кешируешего плагина (DiskCache) получаем практически самое время, что и без использовании данного проекта. Что есть очень хорошо, учитывая гибкость данного подхода.
              0
              Поделился опытом внедрения данного модуля.
              0
              Автор, если на вашем сайте больше 10 пользователей в день, то все печально.
              У нас нагрузка была повыше (> 5000 показов в день), поэтому картинки предварительно резались и клались в CDN, в вашем случае можно просто на диск кидать.
                +2
                Спасибо за здоровый пессимизм. Но на нашем сайте, мы делаем бенчмарки (см. выше).
                На одном ядре отдаётся пимерно 2000 запросов в секунду.

                И второе — не всегда можно знать конечный размер картинки.
                0
                Замечательно! Есть предложение насчет параметра 's' — сделать такое поведение по умолчанию, чтобы картинка не увеличивалась, а если есть параметр 's' пускай scale'ит картинку.
                  0
                  Использование System.Drawing в asp.net-приложениях ведёт к утечкам памяти и иногда к падениям и зависаниям IIS. В MSDN об этом слегка упомянуто :)
                    0
                    Есть такое. По крайней мере в теории. Одновременно с этим imageresizer вроде как GDI+ использует и ничего.
                    Хотя вот тоже планирую переделать на WIC — побыстрее будет.
                      0
                      После определённой нагрузки начинается и на практике :) Обычно с этим борются, вынося проблемный ресайзер в отдельный AppDomain и установку ему рестарта рабочего процесса каждые 5 минут :)

                      Однако я в своём опыте встречал даже падения всего IIS'а в native exception от использования System.Drawing.

                      Ну а на небольших нагрузках «всё пучком», т.к. ресурсы не успевают утечь достаточно сильно между перегрузками рабочего процесса.
                    0
                    Чтобы заработало кеширование необходимо отдавать 304 статус на запросы с заголовками кеширования. В вашем случае надо либо в районе 82 или 104 строки вставить какой-то такой код:
                    string entityTag = Request.Headers["If-None-Match"];
                    DateTime lastModifiedDate = DateTime.TryParseExact(Request.Headers["If-Modified-Since"]
                            ,"r"
                            ,CultureInfo.InvariantCulture
                            ,DateTimeStyles.None
                            ,out lastModifiedDate)
                        ? lastModifiedDate
                        : DateTime.MinValue;
                    if ((!String.IsNullOrEmpty(entityTag) && cacheName == entityTag)
                        || DateTime.UtcNow.Subtract(lastModifiedDate).TotalDays < 1)
                    {
                        Response.StatusCode = 304;
                        Response.StatusDescription = "Not Modified";
                        Response.SuppressContent = true;
                        return;
                    }
                    
                      0
                      PS: ну и да, если фреймворк > 2.0 лучше использовать WPF, неплохой пример для старта есть здесь.

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

                    Самое читаемое