The way I’m doing it here is obsolete, and Returning CSV data to the browser – updated has a better version.

Over on the Rails mailing list, Pete asks:

One of my apps has to export data for the backend system to process it. What’s the best way to create a CSV file in Rails and then return it as
a file to the client?

I have used two tricks in the past to export the data.

Exporting using CSV::Writer

This is simple. Use CSV::Writer and ActionController’s send_data method.

app/controllers/report_controller.rb
 1 class ReportController < ApplicationController
 2   def report
 3     @models = Model.find(:all, :conditions => ['...'])
 4     report = StringIO.new
 5     CSV::Writer.generate(report, ',') do |csv|
 6       csv << %w(Title Total)
 7       @models.each do |model|
 8         csv << [model.title, model.total]
 9       end
10     end
11 
12     report.rewind
13     send_data(report.read,
14       :type => 'text/csv; charset=iso-8859-1; header=present',
15       :filename => 'report.csv')
16   end
17 end

By default, send_data will make the Content-Disposition header equal to attachment. This will ask the browser to download the file, instead of displaying it in the browser window.

Exporting using a regular view

The second method is even simpler:

app/controllers/report_controller.rb
1 class ReportController < ApplicationController
2   def report
3     @models = Model.find(:all, :conditions => ['...'])
4     response.headers['Content-Type'] = 'text/csv; charset=iso-8859-1; header=present'
5     response.headers['Content-Disposition'] = 'attachment; filename=report.csv'
6   end
7 end

app/views/report/report.rhtml
1 Title,Value
2 <% @models.each do |model| -%>
3 "<%= model.title.gsub('"', '""') %>",<%= model.value %>
4 <% end -%>

Notice how I escape quotes in the view ? This is important, or else your parsing will be broken when you read the file back in. Of course, if you use CSV::Writer, you won’t have to muck with that – it will all be taken care of for you.

UPDATE 2006-03-30: Changed Content-Type from text/comma-separated-values to text/csv, and added the header optional parameter, per RFC 4180

UPDATE 2007-04-26: Just noticed that the report method above would render with a layout if one were defined in ApplicationController. Simply call #render and tell it to use no layout: render :action => :report, :layout => false

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on