Make attachment_fu and Refinery CMS work with Amazon S3 EU buckets
Problem: You need to use attachment_fu for uploads in Rails, and would like to store your uploads in an Amazon S3 EU bucket. Amazon updated their API long ago, when they made EU buckets available, but for some reason the defacto standard library for working with S3 from Ruby, AWS::S3, was never updated to support the new API. As a result, you still can’t use the original AWS::S3, if you prefer storing your uploads on Amazons servers in Europe.
Now, there are other libraries that support EU buckets, like RightAWS or S3, but the upload plugin you use (in this case attachment_fu) has to support it. Previously the solution would be to switch to a different upload plugin that used another library, like Paperclip, but last time I checked, Paperclip had replaced RightAWS with AWS::S3. You might also for some reason not be able to switch to a different upload plugin.
On a recent project I was using the excellent Refinery CMS, which comes integrated with attachment_fu. I figured the problem using S3 EU buckets had been long solved, but apparently not, so here is the solution I cooked up:
- Install sauberia’s fork of AWS::S3: http://github.com/sauberia/aws-s3 either as a gem or a plugin in your Rails project.
- Install the attachment_fu plugin (Refinery CMS already has this): http://github.com/technoweenie/attachment_fu
- Patch attachment_fu to generate URLs that work correctly with Amazon S3 EU buckets, by using the code shown below:
- save this as vendor/plugins/attachment_fu_hacks/init.rb
require 'aws/s3'
Technoweenie::AttachmentFu::Backends::S3Backend.module_eval do
# Hacked to use new S3 addressing, which lets us use EU buckets. # Requires sauberia's fork of aws-3s: http://github.com/sauberia/aws-s3 # # All public objects are accessible via a GET request to the S3 servers. You can generate a # url for an object using the s3_url method. # # @photo.s3_url # # The resulting url is in the form: <tt>http(s)://:bucket_name.:server/:table_name/:id/:file</tt> where # the <tt>:server</tt> variable defaults to <tt>AWS::S3 URL::DEFAULT_HOST</tt> (s3.amazonaws.com) and can be # set using the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>. # # The optional thumbnail argument will output the thumbnail's filename (if any). def s3_url(thumbnail = nil) File.join(s3_protocol + bucket_name + '.' + s3_hostname + s3_port_string, full_filename(thumbnail)) end endDisclaimer: I have only tested this with Rails 2.3.5.
Update November 08, 2010:
It seems that Amazon updated the S3 API sometime around the end of October 2010. To successfully upload you now have to use a region-specific endpoint. If you followed this guide, you need to add “server: s3-eu-west-1.amazonaws.com” to your config/amazon_s3.yml file.
See this post for further details: Issues using Amazon S3 European Buckets.