Превью и Resize картинок на лету

    Переделывал сайт заказчику на Netcat и с удивлением обнаружил, что кто-то ещё использует загрузку отдельных картинок для оригиналов и для превьюшек и как следствие отдельные столбцы в таблице БД. Куда ещё не шло создавать превьюшки на стороне сервера после загрузки оригинала.

    Идея простая и не новая. C таким подходом я в первые столкнулся в UMI-CMS, а использовал в RubyOnRails. Смысл в том, что превью создаются только когда они нужны и какого угодно размера, а В БД храниться только название оригинала.

    Если Вам необходимо вывести превью картинки вы вызываете функцию типа:
    1. @thumbs = Photo.view_thumbs('originals_name_file.jpg', '100', 'auto')
    где второй и третий параметр это нужный размер в пикселах (auto значит автоматическая подгонка под массштаб).

    Метод view_thumbs проверяет в папке (например "/images/cache") наличие файла originals_name_file_100xauto.jpg. Если находит то возвращает строку «originals_name_file_100xauto.jpg», если не находит, то создаёт файл нужных размеров на лету и возвращает то же самое.

    Достоинства подхода очевидны:
    1. Не создаётся мусора в виде большого количества превьюшек на диске. Все превью храняться в одной папке «cache» и могут периодически удаляться для освобождения места.
    2. Неограниченное количество превьюшек разных размеров. Достаточно только задать нужные параметры в методе.
    Для cakePHP есть хелпер images.php который можно выдернуть из Bakesale (автоматической подгонки нет). Ниже представлен метод на RubyOnRails реализующий данный подход.

    1. require 'RMagick'

    2. class Photo < ActiveRecord::Base
    3.  
    4.   def self.view_thumbs(image, width = 'auto', height = 'auto')
    5.         img_arr = image.split(".")
    6.     img, img_type = img_arr[0], img_arr[1]
    7.  
    8.     img_thumbs = "#{img}_#{width}x#{height}"
    9.     img_main_dir = "#{RAILS_ROOT}/public/images/"
    10.     img_thumbs_dir = "#{RAILS_ROOT}/public/images/cache/"

    11.     begin
    12.       img_thumbs = Magick::Image.read("#{img_thumbs_dir}/#{img_thumbs}.#{img_type}")
    13.     rescue Magick::ImageMagickError  # Вся соль тут. Если нет нужного тхумбса, то после чтения вываливается ошибка, которую мы спасаем. Если всё нормально, то код далее не выполняется
    14.       img_orig = Magick::Image.read("#{img_main_dir}/#{image}").first
    15.       img_size = {:main =>{:cols => img_orig.columns,:rows => img_orig.rows},
    16.         :thumb =>{:cols =>0.0, :rows =>0.0}
    17.       }
    18.       if 'auto' == width and 'auto' == height
    19.         img_size[:thumb][:rows] = img_size[:main][:rows]
    20.         img_size[:thumb][:cols] = img_size[:main][:cols]
    21.       end
    22.       if 'auto' != width and 'auto' == height
    23.         img_size[:thumb][:rows] = ((width.to_f/img_size[:main][:cols])*img_size[:main][:rows]).to_i
    24.         img_size[:thumb][:cols] = width.to_i
    25.       end
    26.       if 'auto' == width and 'auto' != height
    27.         img_size[:thumb][:rows] = height.to_i
    28.         img_size[:thumb][:cols] = ((height.to_f/img_size[:main][:rows])*img_size[:main][:cols]).to_i
    29.       end
    30.       if 'auto' != width and 'auto' != height
    31.         img_size[:thumb][:rows] = height.to_i  
    32.         img_size[:thumb][:cols] = width.to_i        
    33.       end

    34.       img_new = img_orig.resize!(img_size[:thumb][:cols].to_i, img_size[:thumb][:rows].to_i)
    35.       img_new.write "#{img_thumbs_dir}/#{img_thumbs}.#{img_type}"
    36.     end
    37.     img_thumbs = x
    38.     return "#{img_thumbs}.#{img_type}"
    39.   end
    40. end

    Практика показывает, что указывание только одного размера в методе с автоматическим подгоном другого недостаточно. Если вы ограничиваете только по ширине, то обязательно попадётся картинка слишком высокая и вся вёрстка может съехать. То же самое с высотой. А в приведённом выше коде при указании одновременно и высоты и ширины ресайз будет без сохранения масштаба. Ниже приведён кусок кода на php, который реализует ресайз с ограничением и по высоте и по ширине с сохранением массштаба.

    1. $img_size = array(
    2.                         'main'=>array('width'=>imagesx($img_src), 'height'=>imagesy($img_src)),
    3.                         'thumb'=>array('width'=>0, 'height'=>0)
    4.                 );
    5.                
    6.                 if ('auto' == $width && 'auto' == $height) {
    7.                         $img_size['thumb']['height'] =(int) $img_size['main']['height'];
    8.                         $img_size['thumb']['width'] =(int) $img_size['main']['width'];
    9.                 }
    10.                 else if ('auto' != $width && 'auto' == $height) {
    11.                         $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
    12.                         $img_size['thumb']['height'] = (int) round(($img_size['thumb']['width']/$img_size['main']['width'])*$img_size['main']['height']);
    13.                 }
    14.                 else if ('auto' == $width && 'auto' != $height) {
    15.                         $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
    16.                         $img_size['thumb']['width'] = (int) round(($height/$img_size['main']['height'])*$img_size['main']['width']);
    17.                 }
    18.                 else if ('auto' != $width && 'auto' != $height) {
    19.                         $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
    20.                         $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
    21.                         $Kt = $img_size['thumb']['height']/$img_size['thumb']['width'];//5/1
    22.                         $Km = $img_size['main']['height']/$img_size['main']['width'];//5/1

    23.                         if ($Kt > $Km){
    24.                                 $img_size['thumb']['height'] = $img_size['thumb']['width']*$Km;
    25.                         }
    26.                         else if ($Kt < $Km) {
    27.                                 $img_size['thumb']['width'] = $img_size['thumb']['height']/$Km;
    28.                         }
    29.                 }
    ______________________
    Текст подготовлен в Хабра Редакторе от © SoftCoder.ru

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

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

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

      +1
      Думаю папку cash лучше будет переименовать в cache
        0
        Айм сорри за мой французский :)
        0
        20.   if 'auto' == width and 'auto' == width
        

        Завис на этой строчке
          0
          Надо поправить if 'auto' == width and 'auto' == height.
          +1
          Вы указали достоинства, но есть еще и недостатки (пусть и не критичные)

          Например:
          — нагрузка да сервер при генерации превьюшек
          — Случай, когда лучше сделать превьюшку вручную, например, большая фотография с множеством мелких объектов при автоматическом ресайзе превратиться в кашу. А в ручную можно былобы сделать кроп основной части.

          Ну а так — спасибо и респект =)
            0
            Ресайз запускается только если картинка ещё не ресайзилась (то есть только один раз). А вообще конечно ограничение на размер картинок надо ставить и по весу и по размеру.
              0
              > Ресайз запускается только если картинка ещё не ресайзилась

              правильно, превьюшки сохраняются в кэшэ.

              Тогда какая разница сделаете вы превьюшки нужного размера сразу после загрузки или только по требованию(которое поумолчанию предпологается)? удаление кэша — зачем его удалять? вижу смысл если только полностью поменялась логика программы и нужно перегенерить все превьюшки заново.
                0
                Ресайз происходит только по требованию. Так удобнее чем сразу после загрузки. Например у вас есть доска объявлений с возможностью для юзера загрузить 5 картинок. После загрузки вам сразу придётся делать 15 а то и 20 превьюх (маленькая, средняя (для страницы с комментариями например), чуть больше маленькой для главной страницы в блок «Последние объявления» и ещё кучу чего может понадобить). А если например у вас дизайн сменился и вам не есть не спать, а надо превьюшки на 2 пиксела уменьшить везде? При данном подходе вы меняете только параметры в функции.
                Кеш удалять может понадобиться. Кто-то запостил объявление, а это объявление уже неделю, скажем, никто не просматривал. Зачем превьюшки деражать на диске не используемые? А если кто то вдруг зайдёт то создадуться снова превьюхи, но не все 20, а только те которые на странице с объявлением (без средних и тех что для главной).
                  0
                  тема спорная. Все может меняться в зависимости от программы и необходимого количества превьюшек. Но в вашем ходе мыслей я тоже вижу смысл и не говорю что неправильно :)
                    0
                    >Ресайз происходит только по требованию.
                    Мне кажется, что стоило это более ясно указать. А так складывается впечатление, что идет призыв генерить превьшки при каждом запросе с клиента, за что, понятное дело, нужно сразу расстреливать.
                  0
                  Да ещё и ввести ограничение на число потоков конверсии. А то начнут сто пользователей жать F5 на десятке новых страниц — и привет серверу :) Особенно чревата ситуация, когда N пользователей запросят одну картинку до того, как сгенерируется её превью. Запустится сразу N процессов генерации. Результат может быть весьма парадоксальным, вплоть до повреждения картинок…



                  Уже несколько лет пользуюсь генерацией картинок по запросу, нашёл довольно много граблей :)
                0
                Я решил свой ответ топиком оформить ))
                  0
                  Оригинальное решение :)
                  –1
                  ИМХО бесполезняк с этим мучиться.

                  Придет поисковик полазит по всем страничкам, нагенерит сервер по его запросам кучу картинок, по количеству равной количеству оригиналов.

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

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