In my last blog about paperclip I described the rotation of images.
Since I have added more styles with different sizes for the attachment, I got some performance problems, because for every image, 7 sizes has to be reprocessed and this took a long of time.

My solution is, to handle the reprocessing in background with delayed_job and aasm (acts_as_state_machine).

Media-Model
This is the “normal” Media-Model. First at all, there is AASM included, to see the current state (pending, error, ready) of processing. Furthermore, to show the image, I create a url method, which give back the path to the image (if the state is :ready) or an placeholder (if the state is NOT :ready).

media.rb

class Media

  has_attached_file( :source,
	:styles => {
	:bigger => '1600x1600>',
	:big => '800x600>',
	:album => '560x420>',
	:album_preview => '150x150>',
	:album_folder => '70x70#',
	:profile => '180x250>',
	:avatar => '50x50#' },
	:storage => :filesystem,
  )

  include AASM

  aasm_column :status
  aasm_initial_state :pending

  aasm_state :pending
  aasm_state :error
  aasm_state :ready

  aasm_event :converted do
	transitions :to => :ready, :from => [:pending]
  end

  def url(style = :original)
    if(self.ready?)
      source.url(style)
    else
      # Image is processing, please wait
      Media.find(3).source.url(style)
    end
  end
end

 

The “Uploading” Media-Model
For uploading new images, the model MediaUpload will be used.
This model inherits from the Media-Model, but the paperclip configuration will be overriden.
There is only one style available, because it’s the feedback image for the uploading user.
After saving the image, a new MediaJob will be created in background with the help of delayed_job.

media_upload.rb

class MediaUpload < Media

  # paperclip
  has_attached_file( :source,
    :styles => { :avatar => '50x50#' },
      :storage => :filesystem
  )

  after_create :create_all_styles

  def create_all_styles
    Delayed::Job.enqueue MediaJob.new(self.id)
  end
end

 

The REPROCESSOR
At least, the media-job created all styles for the image …and it’s DONE.

media_jobs.rb

class MediaJob < Struct.new(:id)
  def perform
    m = Media.find(id)
    m.source.reprocess!
  end
end

Helpful links:

In my current app, I’m using paperclip for handling images.

Paperclip includes the option for id_partition, but I prefer an hashed stucture (descriped in my first blog).

For example, the image url looks like

http://my_url/medias/0d/898/0d898414092854eacbf27f8db12ce4db_avatar.jpg

This is the basic configuration in my Media-Model

  # paperclip
  has_attached_file( :source,
    :styles => {
      :original => '1600x1600>',
      :avatar => '50x50#' },
    :storage => :filesystem,
    :url => "/:class/:hashed_public_id/:public_id_:style.:extension",
    :path => ":rails_root/public/:class/:hashed_public_id/:public_id_:style.:extension"
  )

To set the public id, I used the following code and create an md5-hash.
Surely, you can use what you want, for example ActiveSupport::SecureRandom.hex(32).
My set_public_id creates a random string like “6e374ca829eaead5090f6cdd26c08017″.

  def set_public_id
    self.public_id = Digest::MD5.hexdigest(self.source_file_name + self.source_file_size.to_s + rand.to_s + Time.now.to_i.to_s)
  end

For using hashed_public_id or public_id in the paperclip configuration :url => “/:class/:hashed_public_id/:public_id_:style.:extension”, you must set the following attachment interpolations.

  Paperclip::Attachment.interpolations[:public_id] = lambda do |attachment, style|
    # e. g. "6e374ca829eaead5090f6cdd26c08017"
    attachment.instance.public_id
  end

  Paperclip::Attachment.interpolations[:hashed_public_id] = lambda do |attachment, style|
    # e. g. "6e/374"
    File.join(attachment.instance.public_id[0..1], attachment.instance.public_id[2..4])
  end

Furthermore, I need to rotate an image. Therefore, I used the follwing code.
All styles of the image (e. g. original, avatar) will be rotated.

require 'RMagick'
def rotate(degrees)

    if(degrees.class == Fixnum && degrees % 90 == 0)
      each_attachment do |n, a|
        self.source.styles.each do |styles|
          image_path = a.path(styles.first)

          image   = Magick::ImageList.new(image_path)
          image   = image.rotate(degrees)
          image.write(image_path)
        end
        return true
      end
    end
    nil

  end

Helpful links:

Switch to our mobile site