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