I have an application where I need to know when a user's Rails/Paperclip file download is complete. My app is set up to interact with Amazon S3 and I need to run a javascript function when the user has received the completed file.
How can I do this?
Tracking weather or not the download completes is hard, especially in Javascript. There are a few blurred lines in your question which makes me think its not possible.
First, send_file passes a special header to tell the webserver telling it what to send. See the send_file docs. Rails doesn't actually send the file at all, it sets this header which tells the webserver to send the file but then returns immediately, and moves on to serve another request. To be able to track if the download completes you'll have to occupy your Rails application process sending the file and block until the user downloads it, instead of leaving that to the webserver (which is what its designed to do). This is super inefficient.
Next, how can you still be on a page to execute a javascript function if you are downloading a file? Your user clicks the file download link and is taken to wherever the file is, weather that be a send_file from Rails or a redirect to S3 or whatever, they are no longer on the page they came from. If you are thinking about the way Chrome or Firefox works where the download goes into a download manager and the user stays on the page, theres no more interaction with the server on the old page! If you want that page to be notified of download completion, then you'd need a periodic check or long poll to the server to see if the download is done.
I think you'd be better served by redirecting to the S3 file and setting a session variable to redirect the user to where you want them to go after the download is complete so that the next time they visit any page they are back in your planned flow.
Hope this helps!
Related
I am building a document management system in Ruby on Rails that uses Amazon S3 for storage. I am using the carrierwave and carrierwave-aws gems for uploading/downloading the files.
I have it working to where I can generate a presigned url that expires after a certain amount of time (the sooner the better...maybe 10 seconds-1 minute), but the problem I'm having is if someone loads the page and doesn't click the "Download" button right away, then the link expires and they get directed to an ugly XML error.
What I'm trying to figure out is either:
How can I generate the presigned url on the fly when the download button is clicked (I'm thinking with Coffeescript) OR
Go ahead and generate the presigned url on page load, but when download is clicked, somehow check to see if it returns an error and if so, get a new presigned url and redirect to it at that time. (Again, thinking Coffeescript, if possible)
In the past I have taken the following entirely serverside approach to a similar problem (not using carrier wave, but shouldn't be relevant).
Have the download button link to a controller/action in your app, passing the id of the document. Your controller action then generates a presigned URL and then redirects the user to that URL.
In theory you would only need a very short lifetime on such a presigned urls, although if the files are large enough that a user might want to pause and resume transfers then a longer lifetime might be advisable.
For a project, I'll need to know if my user has finished to download a file to delete it on my remote server. Is there a way to do that ?
There are a couple ways of doing this, some more efficient than others, but here is what I've come up with.
Download through your application
If your application is downloading/passing the file through to the user you can trigger a function at the end of the stream to delete the file.
S3 Bucket Access Logging
S3 has access server logs (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html) that log information for each request. Depending on how your application is structured, you may be able to process these to see what's been accessed.
There may be up to a 30-60 minute delay in log availability
Other Options
There are some other options, though perhaps not ideal (without knowing the specifics of your application I don't know whether these are acceptable).
Use Object Expiration (http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectExpiration.html)
Related SO question (PHP instead of ROR, but the concepts should apply) Resumable downloads when using PHP to send the file?
I am trying to build a really easy way for my users to download audio content from aws via my website. Here is the flow:
I give the user a download link. Ex: www.mysite.com/foobar
User clicks on the link.
In my rails controller, I create an expiring aws s3 url and automatically start downloading the audio content from that url.
User's browser should ask the user whether or not to save the file or not. In the event the user accepts to save the file, I want a callback to my rails app to log that the user actually downloaded the file.
So, from a user's perspective, I want the process to be as simple as going to a url I determine, and accepting to download the file when prompted.
In the background, I want to keep the aws s3 url hidden from the user and I want to have the flexibility to write callback logic after the user accepts the download.
What is the recommended way to achieving this?
The best way to solve this is to create an S3 URL with a very short (10 minute?) lifetime and return a redirect to the S3 URL. This does expose the S3 url to the user, but isn't a vulnerability.
If you want to hide the S3 URL, you will need to proxy the download through your servers, which is expensive and consumes a worker process for long periods of time. I do not recommend this, but it is the only way to conceal the S3 resource.
Additionally, if triggering a download vs. a view is important, you need to set the Content-Disposition header to trigger an attachment download:
Content-Disposition: attachment; filename="fname.ext"
Does the send_file method in Rails give a return value? I'd like to do one thing if the sending is successful, and another thing if the sending is not successful. I looked at the documentation at this link, but did not find anything relevant.
No, there is no way to confirm download completion or success with send_file. From this question: Can we find out when a Paperclip download is complete? :
send_file creates the file and then passes a special header to tell
the webserver telling it what to send. Rails doesn't actually send the
file at all, it sets this header which tells the webserver to send the
file but then returns immediately, and moves on to serve another
request. To be able to track if the download completes you'd have to
occupy your Rails application process sending the file and block until
the user downloads it, instead of leaving that to the webserver (which
is what its designed to do). This is super inefficient.
You may be able to do something using cookies and JavaScript on the client.
See this question: Rails File Download And View Update - Howto?
Given an Amazon S3 URL, or any URL that is a direct URL to a file. In my controller, given this URL, I want to send the user the file, whatever it is w/o redirecting.
Is this possible?
If I understand your question correctly, I don't think that's possible from your end. That's why many sites say "right click to save" or something along those lines. Some sites even have links to videos that say "click to download" but when I click the link they start streaming. These are due to MY settings (ie. the settings on the user's client). You can't control that.
If what you're trying to do is HIDE the location of a file...
Send files back to the user - Usually,
static files can be retrieved by using
the direct URL and circumventing your
Rails application. In some situations,
however, it can be useful to hide the
true location of files, particularly
if you're sending something of value
(e-books, for example). It may be
essential to only send files to logged
in users too. send_file makes it
possible. It sends files in 4096 byte
chunks, so even large files can be
sent without slowing the system down.
From an old blog post