3

I have a model that uses Paperclip to handle images. When the image gets uploaded a preview is made for some javascript cropping and then a thumbnail and preview sizes are made from the chosen cropping. Giving us 3 images on S3 total:

  • Original Image
  • Preview (from the user selected cropping)
  • Thumb (from the user selected cropping)

The code in the model for the attachment is:

has_attached_file :picture, ASSET_INFO.merge(
  :whiny => false,
  :styles       => { :thumb => '200>x200#', :preview => '400x300>' },
  :processors   => [:jcropper],
  :preserve_files => true
)

We have some functionality that allows a user to make a copy of an object for their own purposes and we want to copy the images over. I thought that just doing a simple

new_my_model.picture = original_my_model.picture if original_my_model.picture_file_name #no file name means no picture

would get the job done, and it does, but only kind of.

It's copying the picture and then reprocessing the preview and thumbnails based on what's set up in the model.

What I would like to do instead is copy all 3 existing images (original, thumb, and preview) to the new object as they are for the original one and then save them in the appropriate location on S3, skipping the resizing/cropping.

Can anyone point me in the right direction? I've searched online and can't seem to find anything and everything I try doesn't seem to work. Doing a .dup on the original picture causes an exception, so that idea is out.

Koby
  • 7,267
  • 2
  • 21
  • 16
  • Same problem here, I have also added a comment on the best answer in http://stackoverflow.com/questions/14224080/ruby-copy-a-paperclip-attachment-from-one-model-to-another – Tal Yaniv Jun 18 '14 at 05:07

1 Answers1

3

Manual cropping breaks Paperclip's automatic cropping/resizing scheme. That's okay until you want to duplicate an attachment from one model to another. You have two options: Save the cropping parameters for each style in database, and call "reprocess!" after duplicating (based on the this question).

I have no intention to save cropping data in database, it's totally useless. I decided to blindly duplicate the images myself, directly calling S3. Not optimal, but works:

module Customizable

  def duplicate copy_args
    new_model = self.dup
    copy_args.each {|key, val| new_model[key] = val}
    new_model.save

    s3 = AWS::S3.new(self.image.s3_credentials)
    bucket = s3.buckets[self.image.s3_credentials[:bucket]]

    styles = self.image.styles.keys.insert(0, :original)

    begin
      styles.each do |style|
        current_url = self.image.path(style)
        current_object = bucket.objects[current_url]
        if current_object.exists?
          # actually asking S3 if object exists
          new_url = new_model.image.path(style)
          new_object = bucket.objects.create(new_url)
          # this is where the copying takes place:
          new_object.copy_from(current_object)
          new_object.acl = current_object.acl
        end
      end
    rescue Exception => err
      return err
    end

    return true
  end

end

In my model:

class Product < ActiveRecord::Base

  # ...
  has_attached_file :image, ...
  # ...
  include Customizable

  def customize product_id
    return self.duplicate({:in_sale => false}) #resetting some values to the duplicated model
  end

  # ...
end
Community
  • 1
  • 1
Tal Yaniv
  • 493
  • 6
  • 13
  • not sure what's the point of your `object_name` method when you could use Paperclip's `path(:style)` method to generate the same strings. – Sbbs Sep 18 '14 at 12:48
  • S B - good point, answer edited! I didn't know about this method. – Tal Yaniv Sep 19 '14 at 13:35
  • Isn't new_url just giving you the same as current_url since new_model.image is pointing to the same thing as self.image when you dup it? – krx Aug 04 '16 at 15:33
  • Your comment makes sense. Anyway it's just a temporary, semantic holder for the url, as the S3 object is duplicated and a S3 new URL is generated anyway. – Tal Yaniv Aug 05 '16 at 16:57