Reputation: 77
I have a rails application that has links to download a files from another server. I want to try and prevent the actual file url/locations from being "easily" detectable.
I tried using send_file but this only appears to work with local files?
I then tried with send_data - this only seems to work when first reading the file (e.g. file.open ...). I want to be able to send the file without reading it as some of the files are very large and therefore takes ages and has no user feedback etc...
Basically, I want the same behavior as if I was to simply link to/provide the direct file url: that the download opens and starts from the browser.
As I mentioned The reason I want to handle my downloads via a controller (using send_file/data methods) is that I do want the location of the files to be "easily" detectable.
I hope I have explained this well enough :)
Thanks for any advice
Cheers, Jon.
Upvotes: 0
Views: 2333
Reputation: 1819
So you want to stream the data from a public URL to a client without reading it? You're going to have to read it somehow if you want to send it to a client. A redirect is the best solution here, but you don't like redirects because the public URL is ugly.
You could try streaming the response_body
(which accepts enumerbles) from a Net::HTTP read_body
(which produces an enumerable). That might keep your memory footprint down.
def DownloadController
def show
source = URI('http://www.example.com/some-big-pdf.pdf')
# Tell the client to download the response with given filename
headers['Content-Disposition'] = 'attachment; filename="some-big-pdf.pdf"'
Net::HTTP.start(source.host, source.port) do |http|
req = Net::HTTP::Get.new source
http.request(req) do |res|
self.response_body = res.read_body
end
end
end
end
Edit: Of course send_data
can also read from buffers:
http.request(req) do |res|
send_data res.read_body, filename: 'some-big-pdf.pdf', disposition: 'download'
end
However, be aware that this will tie up a worker process until complete. You should ensure that you have enough workers to handle simultaneous downloads. You may also want to look into doing this upstream within your webserver (NGINX?) rather than inside Rails.
See Net::HTTP#read_body and ActionController::Metal#response_body= for more details on these methods. See also https://coderwall.com/p/kad56a/streaming-large-data-responses-with-rails
Upvotes: 1