9 March 2009

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

Lumber room
Переделывал сайт заказчику на 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. def self.view_thumbs(image, width = 'auto', height = 'auto')
  4. img_arr = image.split(".")
  5. img, img_type = img_arr[0], img_arr[1]

  6. img_thumbs = "#{img}_#{width}x#{height}"
  7. img_main_dir = "#{RAILS_ROOT}/public/images/"
  8. img_thumbs_dir = "#{RAILS_ROOT}/public/images/cache/"

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

  32. img_new = img_orig.resize!(img_size[:thumb][:cols].to_i, img_size[:thumb][:rows].to_i)
  33. img_new.write "#{img_thumbs_dir}/#{img_thumbs}.#{img_type}"
  34. end
  35. img_thumbs = x
  36. return "#{img_thumbs}.#{img_type}"
  37. end
  38. 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. if ('auto' == $width && 'auto' == $height) {
  6. $img_size['thumb']['height'] =(int) $img_size['main']['height'];
  7. $img_size['thumb']['width'] =(int) $img_size['main']['width'];
  8. }
  9. else if ('auto' != $width && 'auto' == $height) {
  10. $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
  11. $img_size['thumb']['height'] = (int) round(($img_size['thumb']['width']/$img_size['main']['width'])*$img_size['main']['height']);
  12. }
  13. else if ('auto' == $width && 'auto' != $height) {
  14. $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
  15. $img_size['thumb']['width'] = (int) round(($height/$img_size['main']['height'])*$img_size['main']['width']);
  16. }
  17. else if ('auto' != $width && 'auto' != $height) {
  18. $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
  19. $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
  20. $Kt = $img_size['thumb']['height']/$img_size['thumb']['width'];//5/1
  21. $Km = $img_size['main']['height']/$img_size['main']['width'];//5/1

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

Tags:previewresizeimagesruby on railsphp
Hubs: Lumber room
+2
951 10
Comments 14