How to stream an audio file without a direct link - ruby-on-rails

I have a rails web app sitting on an nginx web server. I have some audio files on my server, and I want people to be able to listen to them, and to be able to seek to any part of the audio file to listen from that point.
Simple right?
I was using direct links in the src of my html5 audio element. It worked great. The file could be played and seeking worked.
Enter security and auditability.
My audio files are sensitive. I only want certain people to be able to listen to them. I also need to know each time that they listen to them. Suddenly the public directory isn't going to work.
Enter rails's send_file.
Send_file initially appeared to be exactly what I needed. It allows rails to serve my audio files, and I could keep my files in a protected directory, I could check the current user's permissions, and I could create the appropriate audit trail. Except...
With send_file I can't seek. That is a deal breaker.
A few stackoverflow questions address getting send_file to handle http-range/byte-range requests. The ones I reviewed are:
what is the proper way to serve mp4 files through rails...
rails media file stream accept byte range request through send file ...
After doing more research, I found the following blog post:
https://blog.echoplex.us/2014/08/19/so-you-want-to-stream-videoaudio-with-rails/
tl;dr
don't use rails send_file to serve media. don't try to make it like the stackoverflow questions say you can. Instead, use nginx and X-Accel-Redirect, and end up with a request pipeline that looks like you->nginx->rails->nginx->you
I am considering taking his approach, but didn't know if there was a better way to do this.
What are my options?
(also, you can assume that I'm using the current versions of rails and nginx)

DON'T USE sendfile please. Use X-Accel-Redirect or the advice below.
Nginx secure_link module helps you to serve files straight from disk with private links. No backend required. The full example is here.

Related

On Heroku, can I use Rails to serve a generated audio file back to my React Native front-end?

I'm generating temporary audio files using Rails and storing them in the tmp directory in Heroku. I think want to send the audio back to Expo/React Native to be played. How can I use Rails to serve that data as a POST response?
In my Rails API backend, I connect to a Text-to-Speech API (Google), and generate an mp3 from text. I can verify that these files are being correctly created and stored in tmp as per my POST request. I am missing the conceptual piece of how to return them back.
It's ideal for me that these files are temporary, as I only need them during a single session. I can see a couple of options.
I can save the files as .mp3s in tmp, as I'm currently doing, and find a way to attach them to my POST response.
I get the audio back from the API as binary, so I can skip saving the files as .mp3s, and respond to the POST with the binary as JSON, or something.
???
Play sound in Expo/ React Native
I'm trying to avoid setting up an AWS bucket for this if possible, but if that's the best way to do it, I'm happy to hear that as well.
Thanks!

Best way to serve files?

I'm a novice web developer with some background in programming (mostly Python).
I'm looking for some basic advice on choosing the right technology.
I need to serve files over the internet (mp3's), but I need to implement some
control on the access:
1. Files will be accessible only for authorized users.
2. I need to keep track on how many times a file was loaded, by whom, etc.
What might be the best technology to implement this? That is, should I
learn Apache, or maybe Django? or maybe something else?
I'm looking for a 'pointer' in the right direction.
Thank!
R
If you need to track/control the downloads that suggests that the MP3 urls need to be routed through a Rails controller. Very doable. At that point you can run your checks, track your stats, and send the file back.
If it's a lot of MP3's, you would like to not have Rails do the actual sending of the MP3 data as it's a waste of it's time and ties up an instance. Look into xsendfile where Rails can send a response header indicating the file path to send and apache will intercept it and do the actual sending.
https://tn123.org/mod_xsendfile/
http://rack.rubyforge.org/doc/classes/Rack/Sendfile.html
You could use Django and Lighttpd as a web server. With Lighttpd you can use mod_secdownload, wich enables you to generate one time only urls.
More info can be found here: http://redmine.lighttpd.net/projects/1/wiki/Docs_ModSecDownload
You can check for permissions in your Django (or any other) app and then redirect the user to this disposable URL if he passed the permission check.

Serving files through controllers with partial download support

I need to serve files through grails, only users with permission have access, so I cant serve them with a static link to a container. The system is able to stream binary files to the client without problems,but now (for bandwidth performance issues on the client) I need to implement segmented or partial downloads in the controllers.
Theres a plugin or proven solution to this problem?
May be some kind of tomcat/apache plugin to restrict access to files with certain rules or temporal tickets so I can delegate the "resume download" or "segmented download" problem to the container.
Also i need to log and save stats on the downloads of the users.
I need good performance so, I think doing this in the controller is not good idea.
Sorry bad english.
There is a plugin for apache - https://tn123.org/mod_xsendfile/ It doesn't matter what you're using behind apache at this case. By using this plugin you will respond with special header X-SENDFILE, with path to file to serve, and Apache will take care about actual file downloading for current request.
If you're using Nginx, you have to use X-Accel-Redirect header, see http://wiki.nginx.org/XSendfile

Intercept request for file download and send to Rails app?

I would like to make a Rails app to create a pretty download page for any file requested either through a link or by typing the url of the file. Is there a way to intercept the request for a file in Apache or elsewhere and send it to the app so it can generate the page?
I'd also prefer not to change the url when redirecting to the app, but it doesn't really matter either way.
So to wrap up, turn this: http://files.spherecat1.com/stuff.txt, into this:
http://files.spherecat1.com/download-page-mockup.png
(Image for illustrative purposes only and may not accurately depict final product. It's fun to add disclaimers to everything.)
Do you mean an entire Rails app just for that? Seems like overkill - you can use Sinatra, a single route, and their send_file function with much greater ease. It also deploys on Apache with Passenger.
If you have control over this thing, and you only plan to offer downloads in this way and not in any other, you should probably store the files in some non-public directory and instead allow your Rails/Sinatra app to access and push the files to the user. (Again, with send_file, that's easy.) Why put a file in your web root if your users will never access it that way?

mod_xsendfile alternatives for a shared hosting service without it

I'm trying to log download statistics for .pdfs and .zips (5-25MB) in a rails app that I'm currently developing and I just hit a brick wall; I found out our shared hosting provider doesn't support mod_xsendfile. The sources I've read state that without this, multiple downloads could potentially cause a DoS issue—something I'm definitely trying to avoid. I'm wondering if there are any alternatives to this method of serving files through rails?
Well, how sensitive are the files you're storing?
If you hosted these files somewhere under your app's /public directory, you could just do a meta tag or javascript redirect to the public-facing URL of these files after your users hit some sort of controller action that will update your download statistics.
In this case, your users would probably need to get one of those "Your download should commence in a few moments" pages before the browser would start the file download.
Under this scenario, your Rails application won't be streaming the file out, your web server will, which will give you the same effect as xsendfile. On the other hand, this won't work very well if you need to control access to those downloadable files.

Resources