Streaming CSV Download from Rails 3.2 app
OK, after a bit more research I hacked together the following in my controller. It'll stream if response_body
is given something enumeratable (is that a word?). Also, the server needs to be able to stream (I am using Unicorn on Heroku). I'd like very much to not have all this stuff in the controller, so my next step is to extract it out somehow.
format.csv {
@entries = Entry.all
@columns = ["First Name", "Last Name"].to_csv
@filename = "entries-#{Date.today.to_s(:db)}"
self.response.headers["Content-Type"] ||= 'text/csv'
self.response.headers["Content-Disposition"] = "attachment; filename=#{@filename}"
self.response.headers["Content-Transfer-Encoding"] = "binary"
self.response_body = Enumerator.new do |y|
@entries.each_with_index do |entry, i|
if i == 0
y << @columns
end
y << [entry.first_name, entry.last_name].to_csv
end
end
}
Streaming large gzipped data to browser with minimal resources
Here's an older blog post that shows an example of streaming: http://patshaughnessy.net/2010/10/11/activerecord-with-large-result-sets-part-2-streaming-data
You might also have luck with the new Streaming API and Batches. If I'm reading the documentation correctly, you'd need to do your queries and output formatting in a view template rather than your controller in order to take advantage of the streaming.
As for gzipping, it looks like the most common way to do that in Rails is Rack::Deflator. In older versions of Rails, the Streaming API didn't play well Rack::Deflator. That might be fixed now, but if not that SO question has a monkey patch that might help.
Update
Here's some test code that's working for me with JRuby on Torquebox:
# /app/controllers/test_controller.rb
def index
respond_to do |format|
format.csv do
render stream: true, layout: false
end
end
end
# /app/views/test/index.csv.erb
<% 100.times do -%>
<%= (1..1000).to_a.shuffle.join(",") %>
<% end -%>
# /config/application.rb
module StreamTest
class Application < Rails::Application
config.middleware.use Rack::Deflater
end
end
Using that as an example, you should be able to replace your view code with something like this to render your CSVName,Created At
<% Model.scope.find_each do |model| -%>
"<%= model.name %>","<%= model.created_at %>"
<% end -%>
As far as I can tell, Rails will continue to generate the response if the user hits stop half-way through. I think this is a limitation with HTTP, but I could be wrong. This should meet the rest of your requirements, though. Export to CSV File
Controller:
respond_to do |format|
format.csv { send_data @report.to_csv, filename: @report.name + ".csv"} #to export csv
end
In the view<%= link_to 'CSV', report_path(report, format: :csv)%>
Ruby on Rails 3: Streaming data through Rails to client
It looks like this isn't available in Rails 3
https://rails.lighthouseapp.com/projects/8994/tickets/2546-render-text-proc
This appeared to work for me in my controller:
self.response_body = proc{ |response, output|
output.write "Hello world"
}
Related Topics
Rails Validating Search Params
How to Access Parent/Sibling Module Methods
Can't Install Debugger Gem - Rails - MAC Osx Mavericks
Accessing Microsoft Exchange Server from Ruby
Using Ruby with Mechanize to Log into a Website
How to Print a Multi-Dimensional Array in Ruby
Remove Adjacent Identical Elements in a Ruby Array
Ruby: Start Reading at Arbitrary Point in Large File
Docker-Compose Restart Connection Pool Full
What Does Bundle Install -Without Production Do
Phusion Passenger with Ruby 1.8 and 1.9
Ruby - Generate All Two Letter Words
How to Fix a Deadlock in Join() in Ruby