Using one carrierwave image uploader with dynamic versions
Posted by Geremia Taglialatela on 22 March 2014
I was looking for a cleaner approach to manage models with different image versions. I came into this awesome blog post by Andrea Pavoni. I needed multiple, configurable processes so I extended and updated his excellent work.
Update! November, 1st 2014
Improved the code with an initializer. This fixes an edge case with Rails Admin (versions are not created when validations fails). Now we will not pollute models with image versions and we can use the same versions for multiple models but there is a little bit of separation as a drawback.
We also improved the image uploader in order to provide progressive jpegs.
image_versions_.rb (initializer)
APP_IMAGE_VERSIONS = {
asset: {
fill_1200x518: {
resize_to_fill: [1200, 518]
},
black_and_white: {
resize_to_limit: [200, 200],
desaturate: nil
}
},
gallery: {
fill_800x600: {
resize_to_fill: [800, 600]
}
}
}
APP_ALL_IMAGE_VERSIONS = APP_IMAGE_VERSIONS.map{ |k,v| v.keys }.flatten.uniq
slideshow_image.rb
class SlideshowImage < ActiveRecord::Base
IMAGE_VERSIONS = APP_IMAGE_VERSIONS[:asset]
mount_uploader :image, ImageUploader
end
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process resize_to_limit: [2048, 2048]
process :optimize
# better to always have a thumbnail version because it is used by Rails Admin
version :thumbnail do
process resize_to_fit: [300, 300]
end
APP_ALL_IMAGE_VERSIONS.each do |v|
version v, if: -> (uploader, opts) { uploader.model.class::IMAGE_VERSIONS.has_key?(opts[:version]) } do
process dynamic_version: v
process :optimize
end
end
def dynamic_version(version)
model.class::IMAGE_VERSIONS[version].each do |method, args|
self.send method, *args
end
end
# Note: this process requires a recent version of ImageMagick
def desaturate
manipulate! do |img|
img.colorspace('Gray')
img.brightness_contrast('20x0')
img = yield(img) if block_given?
img
end
end
def optimize
manipulate! do |img|
img.combine_options do |c|
c.strip
c.quality '85'
c.depth '8'
c.interlace 'Line'
end
img
end
end
def extension_white_list
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
Suggestions to improve this are always appreciated!