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</span> = <span class="co">Model</span>.find(<span class="sy">:all</span>, <span class="sy">:conditions</span> =&gt; [<span class="s"><span class="dl">'</span><span class="k">...</span><span class="dl">'</span></span>]) <span class="no"> 4</span> report = <span class="co">StringIO</span>.new <span class="no"> <strong>5</strong></span> <span class="co">CSV</span>::<span class="co">Writer</span>.generate(report, <span class="s"><span class="dl">'</span><span class="k">,</span><span class="dl">'</span></span>) <span class="r">do</span> |csv| <span class="no"> 6</span> csv &lt;&lt; <span class="s"><span class="dl">%w(</span><span class="k">Title Total</span><span class="dl">)</span></span> <span class="no"> 7</span> <span class="iv">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 =&gt; :report, :layout =&gt; 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