Home > Rails > Rails Fast(er) Clone/Copy of attachment_fu Images

Rails Fast(er) Clone/Copy of attachment_fu Images

October 12th, 2008

We regularly copy images around on our site — for cropping, for duplicating items, and for many other purposes. For the last six months, we’ve been using the code that I found on Ruby forum. It has been passable, but as I’ve been stamping out the bottlenecks in our performance, I’ve found that image duplication has been one of our slower movers. Copying a single image with its two thumbnails was taking about 5-10 seconds per image. Given that all it should be doing, functionally, is making a file copy and a new database entry, this didn’t seem right. I did some research into it today and figured out why.

The reason is that the code on Ruby forum still relies on the main image creating its thumbnails from scratch. That is, the main loop of thumbnail creation in their method isn’t actually saving the thumbnails. The thumbnails get saved on the c.save line, via the standard attachment_fu after_save callback.

I just finished an updated monkey patch that I’m sharing here that should let you copy images without the costly thumbnail re-processing. You can grab attachment_fu mixin.rb or copy the ugly Wordpress version below.


module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module InstanceMethods
attr_writer :skip_thumbnail_processing

# Added by WBH from http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/e55260596398bdb6/4f75166df026672b?lnk=gst&q=attachment_fu+copy&rnum=3#4f75166df026672b
# This is intended to let us make copies of attachment_fu objects
# Note: This makes copies of the main image AND each of its prescribed thumbnails

def create_clone
c = self.clone

self.thumbnails.each { |t|
n = t.clone
img = t.create_temp_file
n.temp_path = img #img.path -- Commented so that img wo'nt get garbage collected before c is saved, see the thread, above.
n.save_to_storage
c.thumbnails<<n
}

img = self.create_temp_file

# We'll skip processing (resizing, etc.) the thumbnails, unless the thumbnails array was empty. If the array is empty,
# it's possible that the thumbnails for the main image might have been deleted or otherwise messed up, so we want to regenerate
# them from the main image
c.skip_thumbnail_processing = !self.thumbnails.empty?
c.temp_path = img #img.path
c.save_to_storage

c.save
return c
end

protected

# Cleans up after processing. Thumbnails are created, the attachment is stored to the backend, and the temp_paths are cleared.
def after_process_attachment
if @saved_attachment
if respond_to?(:process_attachment_with_processing) && thumbnailable? && !@skip_thumbnail_processing && !attachment_options[:thumbnails].blank? && parent_id.nil?
temp_file = temp_path || create_temp_file
attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
end
save_to_storage
@temp_paths.clear
@saved_attachment = nil
callback :after_attachment_saved
end
end

Include it in your environment.rb and you should be golden.

Notice that it is nearly identical to the Ruby forum code, except that it adds a new member variable to the object being copied so it will just save, not re-process, the thumbnails.

I’ve tested it on my dev machine for a few hours this afternoon and it all seems working, I’ll post back later if I encounter any problems with it. You can do the ame.

Bill Rails

  1. October 30th, 2008 at 15:18 | #1

    Great.

    Thnx for this :)

  2. you
    February 16th, 2009 at 21:34 | #2

    Hi,
    I tried your script but I always get undefined method temp_path on

    n.temp_path = img #img.path — Commented so that img wo’nt get garbage

    Any help?

    Thanks,

  3. February 21st, 2009 at 19:34 | #3

    Ah, apparently it relies on some other mixed in attachment_fu methods we’re using. See if adding this to the mixin helps:

    # Adds a new temp_path to the array. This should take a string or a Tempfile. This class makes no
    # attempt to remove the files, so Tempfiles should be used. Tempfiles remove themselves when they go out of scope.
    # You can also use string paths for temporary files, such as those used for uploaded files in a web server.
    def temp_path=(value)
    temp_paths.unshift value
    temp_path
    end

  1. No trackbacks yet.