Streaming Files in Rails
Rails has send_data
and send_file
. The latter works with blobs of data and the former works with files on the server. Blobs of data only work if you already have that in memory and files breakdown once you have multiple servers. What if you want to stream out to the browser as you read in from the database?
You can create a streamer object to hand off data as you fetch it from the database. You just need to set the response body to an object that response to each
.
Put this in your controller:
def download
report = StoredReport.first
headers["Content-Type"] = "text/csv"
headers["Content-disposition"] = "attachment; filename=test.csv"
self.response_body = StoredReportStreamer.new(report)
end
Be sure to include the header stuff to mark it as the correct type and as a download.
Then for the streamer object use this:
class StoredReportStreamer
attr_accessor :report
def initialize(report)
self.report = report
end
def each
report.rows.each do |row|
yield row
end
end
end
That will yield each row of the report out to the browser.
Thanks to this blog post for the tip: http://blog.sparqcode.com/2012/02/04/streaming-data-with-rails-3-1-or-3-2/
Written by Sean Todd
Related protips
4 Responses
In Rails 4 there is new approach: https://gist.github.com/lexmag/3259481
And sinatra example: https://gist.github.com/lexmag/3260374
You specify this for the content-type:
response.headers['Content-Type'] = 'text/event-stream'
Doesn't this only go to WebSocket stuff and not a file? My tip is specifically geared towards streaming to a file download.
Anyways, thanks for the examples.
Yes, my examples aren't about file streaming.
text/event-stream
is for Server-Sent Events. I think worth to understand the approach of streaming in general.
How do you catch an exception inside 'each' method and show it nicely to user?. I have this issue: http://stackoverflow.com/questions/31681519/ruby-on-rails-3-streaming-data-and-catching-exception