如何在操作中生成CSV文件?

18

我试图生成包含来自数据库数据的CSV输出。我想将这些数据提供给第三方,所以我想要把一个URL (website.com/api_data/cars) 给某人,并通过访问此URL,该人将能够使用这些数据 - 我认为我想要访问URL,然后在操作中看到用 ,; 分隔的数据。

但是如何做到这一点呢?

到目前为止,我尝试以下方法:

csv_string = CSV.generate do |csv|
  cols = ["column one", "column two", "column three"]
  csv << cols
  csv << ["A", "B", "C"]

  @filename = "data-#{Time.now.to_date.to_s}.csv"  
end
send_data(csv_string, :type => 'text/csv; charset=utf-8; header=present', :filename => @filename)  

这是在控制器 generate_data 和动作 csv_cars 中。

当我运行这个动作 (webste.com/generate_data/csv_cars),它会自动弹出一个窗口下载文件。

但是如何将 CSV 内容写入动作中呢?这样当我打开 URL 时,就能在其中看到来自数据库的内容了吗?


1
看这个 gem,它叫做 render_csv。我在相同的情况下使用它。 - Roman Kiselenko
但是我应该向 csv_cars 文件写入一些内容吗?另外,我应该使用 send_data 结构吗?(顺便说一句,感谢您的快速回复) - user984621
send_data - [将给定的二进制数据发送到浏览器。此方法类似于render:text => data,但还允许您指定浏览器是否应将响应显示为文件附件(即在下载对话框中)或作为内联数据。您还可以设置内容类型、表面文件名和其他内容。](http://apidock.com/rails/ActionController/Streaming/send_data) - Roman Kiselenko
3个回答

22

我知道这是一个旧的帖子,但我在搜索时遇到了它,所以如果有人做同样的事情,这是我的答案和对我有用的方法。

我认为bhanu的方法很好,但我改变了一些东西。而不是在respond_to内部使用@cars,我只是调用了send_data Cars.to_csv,因为正如Rob所说,它是作为类方法创建的。这对我非常有效。

class Car < ActiveRecord::Base
  def self.to_csv(make)
    attributes = %w{id name price} #customize columns here
    cars = Car.where(maker_name: make)

    CSV.generate(headers: true) do |csv|
      csv << attributes

      cars.each do |car|
        csv << attributes.map{ |attr| car.send(attr) }
      end
    end
  end
end

然后在控制器中

class CarsController < ApplicationController
  def index
    send_data Cars.to_csv('Chevy'), filename: "cars-#{Date.today}.csv"
  end
end

我明白这会在你进入cars/index时被调用,但你可以将它放入任何方法、if语句或任何你想要的地方,并在那里随时调用它。你也可以像我上面所做的那样带参数,并查询特定字段。这绝对比我预想的要容易得多。希望能对某些人有所帮助。


headers: true 选项在 CSV.generate 中是什么意思?它的作用是什么?我在文档中找不到相关说明。 - collimarco

6
请在您的模型中定义一个to_csv方法,如下所示。
class Car < ActiveRecord::Base
  def self.to_csv
    attributes = %w{id name price} #customize columns here

    CSV.generate(headers: true) do |csv|
      csv << attributes

      all.each do |car|
        csv << attributes.map{ |attr| car.send(attr) }
      end
    end
  end
end

在您的控制器中稍后

class CarsController < ApplicationController
  def index
    @cars = Car.all

    respond_to do |format|
      format.html
      format.csv { send_data @cars.to_csv, filename: "cars-#{Date.today}.csv" }
    end
  end
end

这怎么可能行得通?def self.to_csv定义了一个类方法,@cars.to_csv调用了一个实例方法。 - Rob
1
@cars 表示一个活动记录关系,它响应于类方法而不是实例。 - Mark
@bhanu 我该如何将这个进程添加到延迟任务中? - sunil

6

您需要做类似这样的操作。

def csv_cars
  headers = ['column one', 'column two', 'column three']

  csv_data = CSV.generate(headers: true) do |csv|
    csv << headers
    csv << ["A", "B", "C"]
  end

  send_data csv_data, filename: "data-#{Date.today.to_s}.csv", disposition: :attachment
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接