Question

I have a file already on S3 that I'd like to associate to a pre-existing instance of the Asset model.

Here's the model:

class Asset < ActiveRecord::Base
  attr_accessible(:attachment_content_type, :attachment_file_name,
                 :attachment_file_size, :attachment_updated_at, :attachment)

  has_attached_file :attachment, {
    storage: :s3,
    s3_credentials: {
      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
    },
    convert_options: { all: '-auto-orient' },
    url: ':s3_alias_url',
    s3_host_alias: ENV['S3_HOST_ALIAS'],
    path: ":class/:attachment/:id_partition/:style/:filename",
    bucket: ENV['S3_BUCKET_NAME'],
    s3_protocol: 'https'
  }
end

Let's say the path is assets/attachments/000/111/file.png, and the Asset instance I want to associate with the file is asset. Referring at the source, I've tried:

options = {
    storage: :s3,
    s3_credentials: {
      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
    },
    convert_options: { all: '-auto-orient' },
    url: ':s3_alias_url',
    s3_host_alias: ENV['S3_HOST_ALIAS'],
    path: "assets/attachments/000/111/file.png",
    bucket: ENV['S3_BUCKET_NAME'],
    s3_protocol: 'https'
  }
# The above is identical to the options given in the model, except for the path

Paperclip::Attachment.new("file.png", asset, options).save

As far as I can tell, this did not affect asset in any way. I cannot set asset.attachment.path manually.

Other questions on SO do not seem to address this specifically. "paperclip images not saving in the path i've set up", "Paperclip and Amazon S3 how to do paths?", and so on involve setting up the model, which is already working fine.

Anyone have any insight to offer?

Was it helpful?

Solution

As far as I can tell, I do need to turn the S3 object into a File, as suggested by @oregontrail256. I used the Fog gem to do this.

s3 = Fog::Storage.new(
        :provider => 'AWS',
        :aws_access_key_id => ENV['AWS_ACCESS_KEY_ID'],
        :aws_secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
      )

directory = s3.directories.get(ENV['S3_BUCKET_NAME'])
fog_file = directory.files.get(path)

file = File.open("temp", "wb")
file.write(fog_file.body)
asset.attachment = file
asset.save
file.close

OTHER TIPS

Paperclip attachments have a copy_to_local_file() method that allows you to make a local copy of the attachment. So what about:

file_name = "temp_file"
asset1.attachment.copy_to_local_file(:style, file_name)
file = File.open(file_name)
asset2.attachment = file
file.close
asset2.save!

Even if you destroy asset1, you now have a copy of the attachment saved by asset2 separately. You probably want to do this in a background job if you're doing many of them.

Credit to this answer too: How to set a file upload programmatically using Paperclip

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top