diff --git a/amazon_s3.yml.tpl b/amazon_s3.yml.tpl index 81cb807f..5c585e78 100644 --- a/amazon_s3.yml.tpl +++ b/amazon_s3.yml.tpl @@ -2,13 +2,16 @@ development: bucket_name: appname_development access_key_id: secret_access_key: + distribution_domain: XXXX.cloudfront.net test: bucket_name: appname_test access_key_id: secret_access_key: + distribution_domain: XXXX.cloudfront.net production: bucket_name: appname access_key_id: secret_access_key: + distribution_domain: XXXX.cloudfront.net diff --git a/lib/technoweenie/attachment_fu.rb b/lib/technoweenie/attachment_fu.rb index 5deb571b..3c126250 100644 --- a/lib/technoweenie/attachment_fu.rb +++ b/lib/technoweenie/attachment_fu.rb @@ -52,6 +52,8 @@ module ActMethods # for the S3 backend. Setting this sets the :storage to :file_system. # * :storage - Use :file_system to specify the attachment data is stored with the file system. Defaults to :db_system. + # * :cloundfront - Set to true if you are using S3 storage and want to serve the files through CloudFront. You will need to + # set a distribution domain in the amazon_s3.yml config file. Defaults to false # * :bucket_key - Use this to specify a different bucket key other than :bucket_name in the amazon_s3.yml file. This allows you to use # different buckets for different models. An example setting would be :image_bucket and the you would need to define the name of the corresponding # bucket in the amazon_s3.yml file. @@ -80,6 +82,7 @@ def has_attachment(options = {}) options[:thumbnails] ||= {} options[:thumbnail_class] ||= self options[:s3_access] ||= :public_read + options[:cloudfront] ||= false options[:content_type] = [options[:content_type]].flatten.collect! { |t| t == :image ? Technoweenie::AttachmentFu.content_types : t }.flatten unless options[:content_type].nil? unless options[:thumbnails].is_a?(Hash) diff --git a/lib/technoweenie/attachment_fu/backends/s3_backend.rb b/lib/technoweenie/attachment_fu/backends/s3_backend.rb index c7d4c3f7..53b0caf5 100644 --- a/lib/technoweenie/attachment_fu/backends/s3_backend.rb +++ b/lib/technoweenie/attachment_fu/backends/s3_backend.rb @@ -17,22 +17,28 @@ module Backends # If you don't already have your access keys, all you need to sign up for the S3 service is an account at Amazon. # You can sign up for S3 and get access keys by visiting http://aws.amazon.com/s3. # + # If you wish to use Amazon CloudFront to serve the files, you can also specify a distibution domain for the bucket. + # To read more about CloudFront, visit http://aws.amazon.com/cloudfront + # # Example configuration (RAILS_ROOT/config/amazon_s3.yml) # # development: # bucket_name: appname_development # access_key_id: # secret_access_key: + # distribution_domain: XXXX.cloudfront.net # # test: # bucket_name: appname_test # access_key_id: # secret_access_key: + # distribution_domain: XXXX.cloudfront.net # # production: # bucket_name: appname # access_key_id: # secret_access_key: + # distribution_domain: XXXX.cloudfront.net # # You can change the location of the config path by passing a full path to the :s3_config_path option. # @@ -59,6 +65,8 @@ module Backends # * :server - The server to make requests to. Defaults to s3.amazonaws.com. # * :port - The port to the requests should be made on. Defaults to 80 or 443 if :use_ssl is set. # * :use_ssl - If set to true, :port will be implicitly set to 443, unless specified otherwise. Defaults to false. + # * :distribution_domain - The CloudFront distribution domain for the bucket. This can either be the assigned + # distribution domain (ie. XXX.cloudfront.net) or a chosen domain using a CNAME. See CloudFront for more details. # # == Usage # @@ -150,6 +158,16 @@ module Backends # # Niether base_path or full_filename include the bucket name as part of the path. # You can retrieve the bucket name using the bucket_name method. + # + # === Accessing CloudFront URLs + # + # You can get an object's CloudFront URL using the cloudfront_url accessor. Using the example from above: + # @postcard.cloudfront_url # => http://XXXX.cloudfront.net/photos/1/mexico.jpg + # + # The resulting url is in the form: http://:distribution_domain/:table_name/:id/:file + # + # If you set :cloudfront to true in your model, the public_filename will be the CloudFront + # URL, not the S3 URL. module S3Backend class RequiredLibraryNotFoundError < StandardError; end class ConfigFileNotFoundError < StandardError; end @@ -198,6 +216,10 @@ def self.hostname def self.port_string @port_string ||= (s3_config[:port].nil? || s3_config[:port] == (s3_config[:use_ssl] ? 443 : 80)) ? '' : ":#{s3_config[:port]}" end + + def self.distribution_domain + @distribution_domain = s3_config[:distribution_domain] + end module ClassMethods def s3_protocol @@ -211,6 +233,10 @@ def s3_hostname def s3_port_string Technoweenie::AttachmentFu::Backends::S3Backend.port_string end + + def cloudfront_distribution_domain + Technoweenie::AttachmentFu::Backends::S3Backend.distribution_domain + end end # Overwrites the base filename writer in order to store the old filename @@ -249,7 +275,27 @@ def full_filename(thumbnail = nil) def s3_url(thumbnail = nil) File.join(s3_protocol + s3_hostname + s3_port_string, bucket_name, full_filename(thumbnail)) end - alias :public_filename :s3_url + + # All public objects are accessible via a GET request to CloudFront. You can generate a + # url for an object using the cloudfront_url method. + # + # @photo.cloudfront_url + # + # The resulting url is in the form: http://:distribution_domain/:table_name/:id/:file using + # the :distribution_domain variable set in the configuration parameters in RAILS_ROOT/config/amazon_s3.yml. + # + # The optional thumbnail argument will output the thumbnail's filename (if any). + def cloudfront_url(thumbnail = nil) + "http://" + cloudfront_distribution_domain + "/" + full_filename(thumbnail) + end + + def public_filename(*args) + if attachment_options[:cloudfront] + cloudfront_url(args) + else + s3_url(args) + end + end # All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an # authenticated url for an object like this: @@ -301,6 +347,10 @@ def s3_hostname def s3_port_string Technoweenie::AttachmentFu::Backends::S3Backend.port_string end + + def cloudfront_distribution_domain + Technoweenie::AttachmentFu::Backends::S3Backend.distribution_domain + end protected # Called in the after_destroy callback