Commit f7284b9e by Joshua Clayton Committed by Prem Sichanugrist

Pass aditional parameters to S3 expiring urls

This adds functionality to add additional params to the querystring for
S3 expiring URLs. The reason for this is if you want to override
response_content_type or response_content_disposition with expiring
URLs, you have to change Amazon's signature, which gets signed before
adding additional options to the querystring. Because it's added later,
and because Amazon's signature includes the full URL, the signature is
bad and the request fails.

To use this feature:

    has_attached_file :avatar,
                      :s3_url_options => { :response_content_disposition => "inline" }

Additionally, you can pass a lambda and it'll be evaluated when the URL
is generated. If, for example, the content type is incorrect in Amazon
(either empty or application/octet-stream), you can effectively cast the
response from Amazon as a particular content type.

    has_attached_file :avatar,
                      :s3_url_options => lambda {|model| { :response_content_type => model.avatar_content_type } }
parent 4e4fa9d4
......@@ -141,7 +141,8 @@ module Paperclip
def expiring_url(time = 3600, style_name = default_style)
if path
s3_object(style_name).url_for(:read, :expires => time, :secure => use_secure_protocol?(style_name)).to_s
base_options = { :expires => time, :secure => use_secure_protocol?(style_name) }
s3_object(style_name).url_for(:read, base_options.merge(s3_url_options)).to_s
end
end
......@@ -159,6 +160,12 @@ module Paperclip
@s3_host_alias
end
def s3_url_options
s3_url_options = @options[:s3_url_options] || {}
s3_url_options = s3_url_options.call(instance) if s3_url_options.is_a?(Proc)
s3_url_options
end
def bucket_name
@bucket = @options[:bucket] || s3_credentials[:bucket]
@bucket = @bucket.call(self) if @bucket.is_a?(Proc)
......
......@@ -350,8 +350,9 @@ class S3Test < Test::Unit::TestCase
end
context "Generating a secure url with an expiration" do
setup do
rebuild_model :storage => :s3,
def build_model_with_options(options = {})
base_options = {
:storage => :s3,
:s3_credentials => {
:production => { :bucket => "prod_bucket" },
:development => { :bucket => "dev_bucket" }
......@@ -360,6 +361,13 @@ class S3Test < Test::Unit::TestCase
:s3_permissions => "private",
:path => ":attachment/:basename.:extension",
:url => ":s3_alias_url"
}
rebuild_model base_options.merge(options)
end
should "use default options" do
build_model_with_options
rails_env("production")
......@@ -373,8 +381,40 @@ class S3Test < Test::Unit::TestCase
@dummy.avatar.expiring_url
end
should "should succeed" do
assert true
should "allow overriding s3_url_options" do
build_model_with_options :s3_url_options => { :response_content_disposition => "inline" }
rails_env("production")
@dummy = Dummy.new
@dummy.avatar = StringIO.new(".")
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_disposition => "inline")
@dummy.avatar.expiring_url
end
should "allow overriding s3_object options with a proc" do
build_model_with_options :s3_url_options => lambda {|attachment| { :response_content_type => attachment.avatar_content_type } }
rails_env("production")
@dummy = Dummy.new
@file = StringIO.new(".")
@file.stubs(:original_filename).returns("5k.png\n\n")
@file.stubs(:content_type).returns("image/png\n\n")
@file.stubs(:to_tempfile).returns(@file)
@dummy.avatar = @file
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_type => "image/png")
@dummy.avatar.expiring_url
end
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment