Allowing User to Download File from S3 Storage
Asked Answered
P

5

16

Right now I am using Amazon S3 and Paperclip which is allowing my users to upload an image that is associated with the event they are creating. My ultimate goal is since others can view this event, to be able to click on the image and have it prompt a SAVE TO their computer. As of now, clicking the link will open the image in a browser window. I rather have it ask for them to download instead. All images only saved on S3, not local. Need to hide exposed s3 url as well if possible or camouflage it

Here is my current setup

Index.html

<%= link_to 'Download Creative', event.creative.url, class: "btn btn-info" %>

Event.rb

has_attached_file :creative,
                :styles => { :thumb => "150x150", :custcreative => "250x75" },
                :path => ":attachment/:id/:style.:extension",
                :s3_domain_url => "******.s3.amazonaws.com",
                :storage => :s3,
                :s3_credentials => Rails.root.join("config/s3.yml"),
                :bucket => '*****',
                :s3_permissions => :public_read,
                :s3_protocol => "http",
                :convert_options => { :all => "-auto-orient" },
                :encode => 'utf8'

Hoping someone can help me out.

Prehension answered 17/10, 2012 at 17:25 Comment(0)
I
17

To make this work, I've just added a new action in the controller, so in your case it could be:

#routes
resources :events do
  member { get :download }
end

#index
<%= link_to 'Download Creative', download_event_path(event), class: "btn btn-info" %>

#events_controller
def download
  data = open(event.creative_url)
  send_data data.read, :type => data.content_type, :x_sendfile => true
end

EDIT: the correct solution for download controller action can be found here (I've updated the code above): Force a link to download an MP3 rather than play it?

Insolvable answered 21/11, 2012 at 11:32 Comment(1)
You are awesome! Took this idea and altered it a bit to work for my application:Prehension
I
30

To avoid extra load to your app (saving dyno's time in Heroku), I would rather do something like this: add this method to your model with the attachment:

def download_url(style_name=:original)
  creative.s3_bucket.objects[creative.s3_object(style_name).key].url_for(:read,
      :secure => true,
      :expires => 24*3600,  # 24 hours
      :response_content_disposition => "attachment; filename='#{creative_file_name}'").to_s
end

And then use it in your views/controllers like this:

<%= link_to 'Download Creative', event.download_url, class: "btn btn-info" %>
Inroad answered 7/5, 2013 at 18:32 Comment(5)
Genius! This worked wonderfully for downloading right from S3, not tying up the Ruby process, not downloading to the server first, and not sending the user away. AwesomeOverthecounter
Plus, it doesn't require another action in the controller.Overthecounter
Read a lot of other answers for downloading files from s3 (send_data and send_file solutions everywhere), but this was most concise and just straight up worked right of the bat with least amount of code.Cb
Can you please explain what is creative and s3_object() .Also what is style_name. Actually I am getting error undefined method creativeAlodium
"creative" is how the attachment is named (see the question), s3_object is a Paperclip method to obtain a reference of a S3 object and the optional style_name param is just in case you want to create a link for a different version (usually a thumbnail or resized image) instead of the original file. You have to replace "creative" with the name of your attachmentInroad
I
17

To make this work, I've just added a new action in the controller, so in your case it could be:

#routes
resources :events do
  member { get :download }
end

#index
<%= link_to 'Download Creative', download_event_path(event), class: "btn btn-info" %>

#events_controller
def download
  data = open(event.creative_url)
  send_data data.read, :type => data.content_type, :x_sendfile => true
end

EDIT: the correct solution for download controller action can be found here (I've updated the code above): Force a link to download an MP3 rather than play it?

Insolvable answered 21/11, 2012 at 11:32 Comment(1)
You are awesome! Took this idea and altered it a bit to work for my application:Prehension
P
3

Now in aws-sdk v2, there is a method :presigned_url defined in Aws::S3::Object, you can use this method to construct the direct download url for a s3 object:

s3 = Aws::S3::Resource.new
# YOUR-OBJECT-KEY should be the relative path of the object like 'uploads/user/logo/123/pic.png'
obj = s3.bucket('YOUR-BUCKET-NAME').object('YOUR-OBJECT-KEY')
url = obj.presigned_url(:get, expires_in: 3600, response_content_disposition: "attachment; filename='FILENAME'")

then in your views, just use:

= link_to 'download', url
Polyploid answered 31/10, 2016 at 9:42 Comment(0)
P
1
event = Event.find(params[:id])
  data = open(event.creative.url)
  send_data data.read, :type => data.content_type, :x_sendfile => true, :url_based_filename => true
end
Prehension answered 27/11, 2012 at 16:27 Comment(0)
J
0

You need to set the "Content-Disposition" to "attachment" in your HTTP response header. I'm not a Rails developer - so just Google it and you'll see plenty of examples - but it probably looks something like this:

    :content_disposition => "attachment"

or

     ...
    :disposition => "attachment"
Jablonski answered 24/10, 2012 at 7:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.