Переделывал сайт заказчику на Netcat и с удивлением обнаружил, что кто-то ещё использует загрузку отдельных картинок для оригиналов и для превьюшек и как следствие отдельные столбцы в таблице БД. Куда ещё не шло создавать превьюшки на стороне сервера после загрузки оригинала.
Идея простая и не новая. C таким подходом я в первые столкнулся в UMI-CMS, а использовал в RubyOnRails. Смысл в том, что превью создаются только когда они нужны и какого угодно размера, а В БД храниться только название оригинала.
Если Вам необходимо вывести превью картинки вы вызываете функцию типа:
Метод view_thumbs проверяет в папке (например "/images/cache") наличие файла originals_name_file_100xauto.jpg. Если находит то возвращает строку «originals_name_file_100xauto.jpg», если не находит, то создаёт файл нужных размеров на лету и возвращает то же самое.
Достоинства подхода очевидны:
Практика показывает, что указывание только одного размера в методе с автоматическим подгоном другого недостаточно. Если вы ограничиваете только по ширине, то обязательно попадётся картинка слишком высокая и вся вёрстка может съехать. То же самое с высотой. А в приведённом выше коде при указании одновременно и высоты и ширины ресайз будет без сохранения масштаба. Ниже приведён кусок кода на php, который реализует ресайз с ограничением и по высоте и по ширине с сохранением массштаба.
Идея простая и не новая. C таким подходом я в первые столкнулся в UMI-CMS, а использовал в RubyOnRails. Смысл в том, что превью создаются только когда они нужны и какого угодно размера, а В БД храниться только название оригинала.
Если Вам необходимо вывести превью картинки вы вызываете функцию типа:
- @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», если не находит, то создаёт файл нужных размеров на лету и возвращает то же самое.
Достоинства подхода очевидны:
- Не создаётся мусора в виде большого количества превьюшек на диске. Все превью храняться в одной папке «cache» и могут периодически удаляться для освобождения места.
- Неограниченное количество превьюшек разных размеров. Достаточно только задать нужные параметры в методе.
- require 'RMagick'
- class Photo < ActiveRecord::Base
-
- def self.view_thumbs(image, width = 'auto', height = 'auto')
- img_arr = image.split(".")
- img, img_type = img_arr[0], img_arr[1]
-
- img_thumbs = "#{img}_#{width}x#{height}"
- img_main_dir = "#{RAILS_ROOT}/public/images/"
- img_thumbs_dir = "#{RAILS_ROOT}/public/images/cache/"
- begin
- img_thumbs = Magick::Image.read("#{img_thumbs_dir}/#{img_thumbs}.#{img_type}")
- rescue Magick::ImageMagickError # Вся соль тут. Если нет нужного тхумбса, то после чтения вываливается ошибка, которую мы спасаем. Если всё нормально, то код далее не выполняется
- img_orig = Magick::Image.read("#{img_main_dir}/#{image}").first
- img_size = {:main =>{:cols => img_orig.columns,:rows => img_orig.rows},
- :thumb =>{:cols =>0.0, :rows =>0.0}
- }
- if 'auto' == width and 'auto' == height
- img_size[:thumb][:rows] = img_size[:main][:rows]
- img_size[:thumb][:cols] = img_size[:main][:cols]
- end
- if 'auto' != width and 'auto' == height
- img_size[:thumb][:rows] = ((width.to_f/img_size[:main][:cols])*img_size[:main][:rows]).to_i
- img_size[:thumb][:cols] = width.to_i
- end
- if 'auto' == width and 'auto' != height
- img_size[:thumb][:rows] = height.to_i
- img_size[:thumb][:cols] = ((height.to_f/img_size[:main][:rows])*img_size[:main][:cols]).to_i
- end
- if 'auto' != width and 'auto' != height
- img_size[:thumb][:rows] = height.to_i
- img_size[:thumb][:cols] = width.to_i
- end
- img_new = img_orig.resize!(img_size[:thumb][:cols].to_i, img_size[:thumb][:rows].to_i)
- img_new.write "#{img_thumbs_dir}/#{img_thumbs}.#{img_type}"
- end
- img_thumbs = x
- return "#{img_thumbs}.#{img_type}"
- end
- end
Практика показывает, что указывание только одного размера в методе с автоматическим подгоном другого недостаточно. Если вы ограничиваете только по ширине, то обязательно попадётся картинка слишком высокая и вся вёрстка может съехать. То же самое с высотой. А в приведённом выше коде при указании одновременно и высоты и ширины ресайз будет без сохранения масштаба. Ниже приведён кусок кода на php, который реализует ресайз с ограничением и по высоте и по ширине с сохранением массштаба.
- $img_size = array(
- 'main'=>array('width'=>imagesx($img_src), 'height'=>imagesy($img_src)),
- 'thumb'=>array('width'=>0, 'height'=>0)
- );
-
- if ('auto' == $width && 'auto' == $height) {
- $img_size['thumb']['height'] =(int) $img_size['main']['height'];
- $img_size['thumb']['width'] =(int) $img_size['main']['width'];
- }
- else if ('auto' != $width && 'auto' == $height) {
- $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
- $img_size['thumb']['height'] = (int) round(($img_size['thumb']['width']/$img_size['main']['width'])*$img_size['main']['height']);
- }
- else if ('auto' == $width && 'auto' != $height) {
- $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
- $img_size['thumb']['width'] = (int) round(($height/$img_size['main']['height'])*$img_size['main']['width']);
- }
- else if ('auto' != $width && 'auto' != $height) {
- $img_size['thumb']['height'] = (($img_size['main']['height'] <= $height) ? $img_size['main']['height'] : $height);
- $img_size['thumb']['width'] = (($img_size['main']['width'] <= $width) ? $img_size['main']['width'] : $width);
- $Kt = $img_size['thumb']['height']/$img_size['thumb']['width'];//5/1
- $Km = $img_size['main']['height']/$img_size['main']['width'];//5/1
- if ($Kt > $Km){
- $img_size['thumb']['height'] = $img_size['thumb']['width']*$Km;
- }
- else if ($Kt < $Km) {
- $img_size['thumb']['width'] = $img_size['thumb']['height']/$Km;
- }
- }
______________________