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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ReportController < ApplicationController
  def report
    @models = Model.find(:all, :conditions => ['...'])
    report = StringIO.new
    CSV::Writer.generate(report, ',') do |csv|
      csv << %w(Title Total)
      @models.each do |model|
        csv << [model.title, model.total]
      end
    end

    report.rewind
    send_data(report.read,
      :type => 'text/csv; charset=iso-8859-1; header=present',
      :filename => 'report.csv')
  end
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:

1
2
3
4
5
6
7
class ReportController < ApplicationController
  def report
    @models = Model.find(:all, :conditions => ['...'])
    response.headers['Content-Type'] = 'text/csv; charset=iso-8859-1; header=present'
    response.headers['Content-Disposition'] = 'attachment; filename=report.csv'
  end
end
1
2
3
4
Title,Value
<% @models.each do |model| -%>
"<%= model.title.gsub('"', '""') %>",<%= model.value %>
<% 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

A picture of me

I am François Beausoleil, a Ruby on Rails coder. During the day, I work on XLsuite. At night, I am interested many things. Read my biography

Tags

(3) (1) (0) (2) (1) (1) (2) (2) (1) (2) (1) (2) (1) (2) (1) (1) (1) (1) (2) (14) (1) (1) (1) (1) (2) (1) (1) (2) (0) (1) (2) (1) (3) (1) (1) (1) (1) (1) (1) (0) (3) (2) (1) (2) (2) (1) (3) (2) (8) (8) (9) (12) (1) (1) (3) (1) (1) (1) (1) (1) (1) (2) (2) (2) (1) (1) (3) (1) (3) (1) (0) (21) (1) (1) (0) (1) (1) (1) (21) (23) (1) (1) (13) (1) (1) (2) (3) (1) (1) (4) (1) (2) (3) (0) (1) (7) (3) (1) (5) (5) (2) (2) (2) (4) (6) (7) (1) (0) (1) (1) (2) (2) (1) (4) (12) (2) (1) (2) (4) (1) (1) (1) (2) (8) (2) (3) (2) (2) (1) (3) (1) (1)

Links

Projects I work on

Categories

Archives