What is the difference between send_data and send_file in Ruby on Rails?

Which one is best for streaming and file downloads?

Please provide examples.


Solution 1:

send_data(_data_, options = {})
send_file(_path_, options = {}) 

Main difference here is that you pass DATA (binary code or whatever) with send_data or file PATH with send_file.

So you can generate some data and send it as an inline text or as an attachment without generating file on your server via send_data. Or you can send ready file with send_file

data = "Hello World!"
send_data( data, :filename => "my_file.txt" )

Or

data = "Hello World!"
file = "my_file.txt"
File.open(file, "w"){ |f| f << data }
send_file( file )

For perfomance it is better to generate file once and then send it as many times as you want. So send_file will fit better.

For streaming, as far as I understand, both of this methods use the same bunch of options and settings, so you can use X-Send or whatever.

UPD

send_data and save file:

data = "Hello World!"
file = "my_file.txt"
File.open(file, "w"){ |f| f << data }
send_data( data )

Solution 2:

send_file may be faster than send_data

As fl00r mentioned, send_file takes a path, and send_data the data.

Therefore send_file is a subset of send_data, as you need a file on the filesystem: you could of course just read the file and use send_data on it. But send_file can be faster, so it is a performance / generality trade-off.

send_file can be faster because it can send the X-Sendfile header on Apache (X-Accel-Redirect on Nginx) instead of the file content, since it knows the path.

This header is consumed by the reverse proxy (Apache or Nginx) which normally runs in front of Rails in a production setup.

If X-Sendfile is present on the response, the reverse proxy ignores most of the current response, and builds a new one that returns the file at the given path.

Client <---> Internet <---> Reverse proxy <---> Rails

This is much more efficient since the reverse proxy is highly specialized at serving static files, and can do it much faster than Rails (which does not send the file data if X-Sendfile will be sent).

The typical use case of send_file is when you want to control the access permission of static files: you cannot put them under /public or else they would get served before Rails has a chance to decide. This is discussed at: Protecting the content of public/ in a Rails app

In order to use the X-Sendfile headers, you have to add:

config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx

to config/initializers/production.rb (or config/environment/production.rb in Rails 5.x), not application.rb, since in development you don't have a proxy server and you want send_file to actually send the data.

X-Sendfile is discussed on the Asset Pipeline Guide.