03 February 2011

Paperclip, S3, and European Buckets

UPDATE @englandpost points out that newer versions of paperclip support the :s3_host_name option, see http://rubydoc.info/gems/paperclip/Paperclip/Storage/S3. Thanks @englandpost


So you have your European S3 bucket thinking how cool you can select buckets near where your customers live, you gem install paperclip aws_s3 and do the dances and the rails and the rituals and the cap production deploy, and you get


The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.

Not Fair!

The interblags reccomend you use gem install s3 instead, with appropriate monkeypatches for Paperclip::Storage::S3, but you might end up with this:


The request signature we calculated does not match the signature you provided. Check your key and signing method.

I tried and I tried, honest, s3 just wouldn't play the signature game by amazon's rules ... couldn't get anything to work, until I stumbled on http://www.mail-archive.com/heroku@googlegroups.com/msg05407.html in which the great and goodly Dan Croak recommends you put this in config/environment.rb:


AWS::S3::DEFAULT_HOST = "s3-eu-west-1.amazonaws.com"

Well lo and behold I was finally able to upload stuff, my pretty pictures are showing up in my AWS console.

But you're not done yet: you still need to generate the correct URL (my_model.my_attachment.url) for your pictures and mp3s and videos and Large Objects and whatever your pushing up to the clouds there ... Paperclip::Storage::S3 kindly hard-codes "s3.amazonaws.com" for you, and it doesn't work.

Here's the fix:


# in config/initializers/something.rb
Paperclip.interpolates(:s3_eu_url) { |attachment, style|
  "#{attachment.s3_protocol}://s3-eu-west-1.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
}

# in your model
has_attached_file :image, 
  :storage => :s3,
  :s3_credentials => "#{Rails.root}/config/s3.yml",
  :path => "for/example/:id/:style.:extension",
  :url  => ":s3_eu_url"

This is a big song and dance to simply tell paperclip how to construct the s3 url. Left to itself, paperclip will replace your url setting with one of its own (":s3_path_url") if it doesn't match /^:s3.*url$/. Hence, the interpolation above is called "s3_eu_url", you can write your own for singapore or whatever far-flung place you've dumped your bucket.

21 comments:

  1. Dude are you serious???? You saved my day!! Thaaaaaanks!

    ReplyDelete
  2. Great stuff. I've been looking for a solution for this all day.

    ReplyDelete
  3. Thanks man! An answer difficult to find...

    ReplyDelete
  4. what about singapure bukets

    ReplyDelete
  5. Thanks a lot, was working around this for quite too long!

    Why it doesn't work with:

    AWS::S3::Base.establish_connection!(
    :access_key_id => '123',
    :secret_access_key => 'XYZ',
    :server => 's3-eu-west-1.amazonaws.com'
    )

    ???

    Their manual could be more explicit!

    Thanks..

    ReplyDelete
  6. Thanks! You saved my day

    ReplyDelete
  7. Many thanks! Been banging my head to the wall for the last hour.

    ReplyDelete
  8. Thanks! This tip helped a lot

    ReplyDelete
  9. Thanks, worked beautifully!
    One small correction: I'm using Paperclip v2.4.0 and it requires "s3_protocol" being called with the "style" parameter.

    ReplyDelete
  10. Anyone got this working with paperclip 2.4.5?

    Are you able to elaborate how to "s3_protocol" being called with the "style" parameter."

    ReplyDelete
  11. I have created storage module for Paperclip that uses official 'aws-sdk' gem. It supports all S3 locations from the box.

    Works well, using it in production projects myself.
    https://github.com/igor-alexandrov/paperclip-aws

    ReplyDelete
  12. Gorgeous !! You made my day !!

    ReplyDelete
  13. thaaaaank you!!!

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  15. now you need just specify s3_host_name *http://rubydoc.info/gems/paperclip/Paperclip/Storage/S3

    ReplyDelete
  16. thank you.it is working very fine

    ReplyDelete
  17. Update, the latest version of paperclip handles most of this for you. All you need is to include:

    :url => ":s3_domain_url"

    in your has_attached_file statement.

    ReplyDelete