Download file on click - Ruby on Rails - ruby-on-rails

My application is using Rails 2 backend, Heroku for hosting, Paperclip for file uploads, and Amazon S3 for file storage.
Right now users can upload files with paperclip + s3 - this works flawlessly. After upload, an icon appears on their dashboard, linked to the file location (in s3 bucket). When the icon is clicked, the browser opens the file in a new window (for most file types - PDF, MP3, img, etc). Instead of opening, I want the file to be automatically downloaded when the user clicks the file's icon (like Gmail attachments). The solution should be able to work for any file type and cross-browser.
Is there a helper to do this in rails, or is javascript needed? I'm really stuck on this one so anything to point me in the right direction would be greatly appreciated. Thanks!

Please try the following:
class Test < ActiveRecord::Base
has_attached_file :testfile,
:storage => :s3,
# All your S3 config
:s3_headers => {"Content-Disposition" => "attachment"}
end
This should tell the Paperclip Gem to set the "Content-Disposition" header to the value "attachment" for newly uploaded files.
Note that you have to manually edit the already uploaded file, e.g. with Cyberduck or another FTP Client.

When you transfer the file, you need to set a Content-Disposition header with a value of attachment; filename=yourfilename.pdf. If it's transfered directly from S3, you'll need to tell S3 to set the Content-Disposition headers as well. Possibly also Content-Type.
Note that if you tell S3 to associate a Content-Disposition header, it will always transmit this header.
FWIW, here's Amazon's documentation on doing a PUT for an Object: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html

Related

Rails s3_direct_upload without file field

My website generates a file in javascript (audio recording) and I then want it to be uploaded to Amazon S3.
I first managed to get the uploading part working by sending the generated file to my server, where it is uploaded. However I would like now to upload the file directly to S3, without going through my server.
So I started to use the s3_direct_upload gem, which works great when using a file_field. However my file is generated by the javascript and :
- The value of a file field has to be set by the user for security reasons
- I do not want the user to have to interact with the upload
I tried to play with the S3Uploader class and to directly add data, without any success for now, it seems that I do not use the correct method.
Does anyone has any idea on how to achieve S3 direct upload without a file field ?
Thanks
Never mind, I found out that the S3Uploader class used by the s3_direct_upload gem has the same methods as the jQuery-File-Upload from which it is derived.
So one can call $("#s3_uploader").fileupload('send', {files: [f]});
And the f File will be uploaded to S3 directly

Setting Content-Disposition to attachment using Ruby on Rails and Paperclip

I have a – hopefully small – problem.
I am using Ruby on Rails and Paperclip to handle file uploads.
Now I want to automatically set the Content-Disposition header to "attachment" so that when the user clicks a link, the file is downloaded instead of shown directly in the browser.
I found the following solution for Amazon S3:
Download file on click - Ruby on Rails
But I don't use S3.
Can anybody help?
Thanks in advance,
/Lasse
If you use File Storage, Paperclip stores the files within the RAILS_ROOT/public/system folder (configurable using the :path option).
Files from the /public folder are served directly as static files. "Rails/Rack never sees requests to your public folder" (to quote cwninja).
The files from the /public folder are served by the webserver running this app (for example Apache or WEBrick in development). And the webserver is responsible for setting the headers upon serving the file. So you should configure the webserver to set the correct headers for your attachment.
Another option is to build a controller or some Rack middleware to serve your paperclip attachments. There you can do something like response.headers['Content-Disposition'] = 'attachment'.
Third option is to use S3, then you can store headers (like Content-Disposition) within the S3-object. S3 then serves the paperclip attachment using those headers.
According to this link, you can do the following:
<Files *.xls> ForceType application/octet-stream Header set Content-Disposition attachment </Files>
<Files *.eps> ForceType application/octet-stream Header set Content-Disposition attachment </Files>

Carrierwave + Fog(s3). Letting the users to download the file

I am working on a project where I have to provide download links to user for the files which are stored in the s3. Initially I tried,
link_to "Download", #medium.file.url
But this opens the file directly on the browser. When i tried to download an mp4 file, chrome started playing it automatically. I don't want that to happen. So I am using send_file for this task,
def download
#medium = Medium.find(params[:id])
send_file #medium.file.url
end
In my local, I have set the storage to file and I have tested this, which works perfectly fine. But on staging, the files are served from s3, I am always getting ActionController::MissingFile. My app is hosted on heroku. I also want to know if using send_file is good choice or if there is a better way of doing this.
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
When i was googling, i found that nginx header setting for sending files should be enabled for production. I added the following line in config/environments/production.rb. Still no luck. I need some help on this. Thanks.
I think this is because S3 wants the file to be played.
I don't have access to my S3 here, but I solved the problem by changing the action for PDF files (which was shown my case) in the S3 control panel.
If you can fix this, I think you can avoid using send_file completely.
Edit: I managed to access S3 now. Go to properties -> Metadata, and add the key
Content-Disposition: Attachment
Then your file will always be downloaded instead of shown.

Alternative to X-sendfile in Apache for sending file given a URL?

I'm writing a Rails application that serves files stored on a remote server to the end user.
In my case the files are stored on S3 but the user requests the file via the Rails-application (hiding the actual URL). If the file was on my servers local file-system, I could use the Apache header X-Sendfile to free up the Ruby process for other requests while Apache took over the task of sending the file to the client. But in my case - where the file is not on the local file-system, but on S3 - it seems that I'm forced to download it temporarily inside Rails before sending it to the client.
Isn't there a way for Apache to serve a "remote" file to the client that is not actually on the server it self. I don't mind if Apache has to download the file for this to work, as long as I don't have to tie up the Ruby process while it's going on.
Any suggestions?
Thomas, I have similar requirements/issues and I think I can answer your problem. First (and I'm not 100% sure you care for this part), hiding the S3 url is quite easy as Amazon allows you to point CNAMES to your bucket and use a custom URL instead of the amazon URL. To do that, you need to point your DNS to the correct amazon URL. When I set mine up it was similar to this: files.domain.com points to files.domain.com.s3.amazonaws.com. Then you need to create the bucket with the name of your custom URL (files.domain.com in this example). How to call that URL will be different depending on which gem you use, but a word of warning was that the attachment_fu plugin I was using was incorrectly sending me to files.domain.com/files.domain.com/name_of_file.... I couldn't find the setting to fix it, so a simple .sub method for the S3 portion of the plugin fixed it.
On to your other questions, to execute some rails code (like recording the hit in the db) before downloading you can simply do this:
def download
file = File.find(...
# code to record 'hit' to database
redirect_to 3Object.url_for(file.filename,
bucket,
:expires_in => 3.hours)
end
That code will still cause the file to be served by S3, but and still give you the ability to run some ruby. (Of course the above code won't work as is, you will need to point it to the correct file and bucket and my amazon keys are saved in a config file. The above is also using the syntax for the AWS::S3 gem - http://amazon.rubyforge.org/).
Second, the Content-Disposition: attachment issue is a bit more tricky. Hopefully, your situation is a bit more simple than mine and the following solution can work. Assuming the object 'file' (in this example) is the correct S3 object, you can set the disposition to attachment by
file.content_disposition = "attachment"
file.save
The above code can be executed after the file exists on the S3 server (unlike some other headers and permissions), which is nice and it can also be added when you upload the file (syntax depends on your plugin). I'm still trying to find a way to tell S3 to send it as an attachment and only when requested (not every time), and if you find that, please let me know your solution. I need to be able to sometimes download it and other times save embed images (for example) into HTML. I'm not using the above mentioned redirect but fortunately it seems that if you embed (such as a HTML image tag) a file with the content-disposition/attachment header, and the browser still displays the image normally (but I haven't throughly tested that across enough browsers to send it in the wild).
Hope that helps! Good luck.

Ruby on rails: Image downloads with Authentication/Authorization/Time outs

I'm having few doubts on implementing file downloads. I'm creating an app where I use attachment_fu with Amazon s3 to upload files. Things are working pretty well so far on uploading side. Now its the time to start the file downloads. Here is what I need, a logged in user search and browse for Images and they should able to add the files in to a download basket (Let's say its a Download Shopping Cart). Finally the user should be able to download these file(s) from S3 probably as a zipped file.
Is there any plugin/gem where I can use for this?
The downside of giving the customer a zip file of all the files is that you'll need to first pull all of the files from S3 back onto your server, then zip them.
You can certainly do that if you want, but it will take a bit of time, you would not want to do it synchronously as part of the browser request. Instead, do it as a background job using delayed_job or similar.
To do the actual zipping, use Zlib::GzipWriter See http://ruby-doc.org/core/classes/Zlib/GzipWriter.html -- it is part of standard Ruby
You could then:
email the user the actual zip file as an attachment
email the user the link to the zip file on your server
or upload the zip file to s3, then email a link to the zip file on s3
Remember to create a clean up task/job to remove the old zip files from your system...
Alternative is to not zip the files together, instead, give the user one or more links to download the files separately.
S3 enables you to create a url to an S3 file that can be used for a set period of time. (The file would be private on S3 so a straight link to it won't work.) Here's how to create it using attachment-fu and aws-s3 gem:
# I added this as a method to my model for the files stored in S3
def authenticated_s3_url
# return a publicly usable url
connect_to_aws # a local method which connects/re-connects to s3
S3Object.url_for(full_filename,
bucket_name,
:expires_in => 60 * 60) # 1 hour
end

Resources