File's metadata saving plugin for carrierwave
Switch branches/tags
Clone or download
Latest commit 62fed95 Oct 22, 2015



Build Status


Add the following line to your Gemfile:

gem 'carrierwave-meta'

Usage example

class TestUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  include CarrierWave::Meta

  process :store_meta => [{md5sum: true}]
  version :version do
    process :resize_to_fill => [200, 200]
    process :store_meta

file ='test.jpg') # JPEG 500x300, 20000 bytes
uploader =!(file)

uploader.width        # 500
uploader.height       # 300
uploader.image_size   # [500, 300]
uploader.file_size    # 20000
uploader.content_type # "image/jpeg"
uploader.md5sum       # "fuuaasdfasdf...."

uploader.version.width        # 200
uploader.version.height       # 200
uploader.version.image_size   # [200, 200]
uploader.version.file_zie     # less than 20000
uploader.version.content_type # "image/jpeg"
uploader.version.md5sum       # nil

Saving values to database

Simply create database columns to hold metadata in your model's table. Currently gem supports width, height, image_size ([width, height]), content_type, file_size and MD5 fields. Versions are supported too.

class TestModel
  attr_accessor :image_width
  attr_accessor :image_height
  attr_accessor :image_image_size
  attr_accessor :image_content_type
  attr_accessor :image_file_size
  attr_accessor :image_md5sum

  attr_accessor :image_version_width
  attr_accessor :image_version_height
  attr_accessor :image_version_image_size
  attr_accessor :image_version_content_type
  attr_accessor :image_version_file_size
  attr_accessor :image_version_md5sum

file ='test.jpg')
model =
uploader =, :image)!(file)

uploader.width      # 500
model.image_width   # 500
model.image_height  # 300

When columns are available in the model instance, metadata is stored in that columns.

Saving values into single column

For now, works only with ActiveRecord.

class TestModel < ActiveRecord::Base
  extend CarrierWave::Meta::ActiveRecord

  mount_uploader :image, TestUploader
  serialize :image_meta, OpenStruct
  carrierwave_meta_composed :image_meta,
    :image, image_version: [:width, :height, :md5sum]

model =!('test.jpg')
model.image_width         # 200
model.image_version_width # 200
model.image_meta          # {image_width: 200, image_height: 200, ...}

All you need is image_meta column, all other attributes are virtual. Note that carrierwave_meta_composed should be called after mounting uploader.

Behind the scenes

After the file is retrieved from store or cache metadata is recalculated unless uploader has attached model instance. If uploader has attached model instance values are read from that instance.

uploader =
uploader.version.width # 200

model =!('test.jpg')
model.image_width # 200
model.image.width # 200, actually read from image_width


Is used to synchronize data between uploader and mounted model instance. Model's instance is used like value cache.

class DelegateTestModel
  attr_accessor :processed
  attr_accessor :a_processed
  attr_accessor :a_b_processed

class DelegateTestUploader < CarrierWave::Uploader::Base
  model_delegate_attribute :uploaded


  version :a do
    version :b do

  def set_processed
    self.processed = true

model =
uploader =, :image)
file ='test.jpg')!(file)

model.processed     # true
model.a_processed   # true
model.a_b_processed # true

model.a_processed = false

uploader.processed     # true
uploader.a_processed   # false
uploader.a_b_processed # true

When model is mounted to uploader:

  1. If attribute is assigned inside uploader then corresponding property

in model is also assigned.

  1. If attribute is retrieved from uploader, uploader checks that value is

defined in model and returns it. Otherwise returns uploader's instance variable.

  1. If file is deleted, value becomes nil.

Otherwise acts as regular uploader's instance variables.

This is very useful for:

Integrating CarrierWave with JCrop

Let implement the behavior like at this demo:

The uploader:

class CropUploader < SobakaUploader
  include CarrierWave::Meta

  # Crop source is a source image converted from original which could be bigger than source area (left image in the example).
  version :crop_source do
    process :resize_to_fit => [300, 300]
    process :store_meta

    # This is the cropped version of parent image. Let crop to 50x50 square.
    version :crop do
      process :crop_to => [50, 50]

  # Defines crop area dimensions.
  # This should be assigned before #store! and #cache! called and should be saved in the model's instance.
  # Otherwise cropped image would be lost after #recreate_versions! is called.
  # If crop area dimensions are'nt assigned, uploader calculates crop area dimensions inside the
  # parent image and creates the default image.
  model_delegate_attribute :x
  model_delegate_attribute :y
  model_delegate_attribute :w
  model_delegate_attribute :h

  # Crop processor
  def crop_to(width, height)
    # Checks that crop area is defined and crop should be done.
    if ((crop_args[0] == crop_args[2]) || (crop_args[1] == crop_args[3]))
      # If not creates default image and saves it's dimensions.
      resize_to_fill_and_save_dimensions(width, height)
      args = crop_args + [width, height]

  def crop_and_resize(x, y, width, height, new_width, new_height)
    manipulate! do |img|
      cropped_img = img.crop(x, y, width, height)
      new_img = cropped_img.resize_to_fill(new_width, new_height)

  # Creates the default crop image.
  # Here the original crop area dimensions are restored and assigned to the model's instance.
  def resize_to_fill_and_save_dimensions(new_width, new_height)
    manipulate! do |img|
      width, height = img.columns, img.rows
      new_img = img.resize_to_fill(new_width, new_height)

      w_ratio = width.to_f / new_width.to_f
      h_ratio = height.to_f / new_height.to_f

      ratio = [w_ratio, h_ratio].min

      self.w = ratio * new_width
      self.h = ratio * new_height
      self.x = (width - self.w) / 2
      self.y = (height - self.h) / 2


  def crop_args
    %w(x y w h).map { |accessor| send(accessor).to_i }

# Post should have :crop_source_version_x, :crop_source_version_y, :crop_source_version_h, :crop_source_version_w columns
class Post < ActiveRecord::Base
  mount_uploader CropUploader, :image

# Let's upload an image
post =
post.image = params[:image] # Let the uploaded file is 800x600 JPEG!

post.image.crop_source.width  # 300
post.image.crop_source.height # 200
post.image.crop_source.crop.width  # 50
post.image.crop_source.crop.height # 50

# Default crop area coordinates within the limits of big image dimensions: square at the center of an image
post.image.crop_source.crop.x # 50
post.image.crop_source.crop.y # 50
post.image.crop_source.crop.w # 200
post.image.crop_source.crop.h # 200

# Let user change the crop area with JCrop script. Pass new crop area parameters to the model.
post.crop_source_crop_x = 100
post.crop_source_crop_y = 100
post.crop_source_crop_w = 100
post.crop_source_crop_h = 100! # Crop image is reprocessed

post.image.crop_source.crop.width  # 50
post.image.crop_source.crop.height # 50

PDF/GhostScript support

If you want to use this plugin with PDF/PostScript files than you should install GhostScript and rebuild ImageMagick with GhostScript support:

brew install ghostscript
brew install imagemagick --with-ghostscript
gem uninstall rmagick && gem install rmagick

To switch on PDF/EPS processing you should enable GhostScript somewhere in your app's initializer:

CarrierWave::Meta.ghostscript_enabled = true

A note about testing

@SergeyKishenin added specs for EPS/GhostScript files. They run for image_magick or mini_magick processor by default. To make specs work please install GhostScript as described above. To run specs WITHOUT PDF/EPS do:

PDF_EPS=false bundle exec rspec

@fschwahn added support for mini-magick. To run tests with mini-magick do:

PROCESSOR=mini_magick bundle exec rspec

@skord added support for ImageSorcery. To run specs do:

PROCESSOR=image_sorcery bundle exec rspec

To run specs against VIPS processor do:

PROCESSOR=vips bundle exec rspec

To run specs against with Fog (Amazon S3) simulation:

STORAGE=fog bundle exec rspec


  1. I do not know how it would work with S3 and other remote storages. Should be tested.

  2. Write integration guide for JCrop.

  3. A notice about content-type.