4

我为用户提供了通过 CSV 下载大量数据的能力。为此,我正在使用 Sidekiq 并在他们启动后将任务放到后台作业中。我在后台工作中所做的是生成一个包含所有正确数据的 csv,将其存储在其中/tmp,然后调用 save!在我的模型上,将文件的位置传递给回形针属性,然后该属性会关闭并存储在 S3 中。

所有这些在本地都运行良好。我现在的问题在于 Heroku,它能够根据您所在的节点在短时间内存储文件。由于 Heroku 处理这些文件的方式,我的后台作业无法找到保存的 tmp 文件。我想我正在寻找更好的方法来做到这一点。如果有某种方式可以在内存中完成所有事情,那就太棒了。唯一的问题是,当您保存模型时,回形针需要一个实际的文件对象作为属性。这是我的背景工作的样子:

class CsvWorker
  include Sidekiq::Worker

  def perform(report_id)
    puts "Starting the jobz!"
    report = Report.find(report_id)
    items = query_ranged_downloads(report.start_date, report.end_date)

    csv = compile_csv(items)

    update_report(report.id, csv)
  end

  def update_report(report_id, csv)
    report = Report.find(report_id)
    report.update_attributes(csv: csv, status: true)
    report.save!
  end

  def compile_csv(items)
    clean_items = items.compact
    path = File.new("#{Rails.root}/tmp/uploads/downloads_by_title_#{Process.pid}.csv", "w")
    csv_string = CSV.open(path, "w") do |csv|
      csv << ["Item Name", "Parent", "Download Count"]
      clean_items.each do |row|
        if !row.item.nil? && !row.item.parent.nil?
        csv << [
          row.item.name,
          row.item.parent.name,
          row.download_count
          ]
        end
      end
    end

    return path
  end
end

为了便于阅读,我省略了查询方法。

4

1 回答 1

1

我不认为 Heroku 的临时文件存储是这里的问题。围绕它的警告主要围绕以下事实: a) dynos 是短暂的,所以你写的任何东西都可以而且会在没有通知的情况下消失;b) dyno 是可互换的,因此当您运行多个 web dyno 时,请求间临时文件的存在是运气问题。但是,在任何情况下,临时文件都不会在您的工作人员运行时消失。

我注意到的一件事是,您实际上是在创建两个同名的临时文件:

> path = File.new("/tmp/filename", "w")
 => #<File:/tmp/filename> 
> path.fileno
 => 3 
> CSV.open(path, "w") do |csv| csv << %w(foo bar baz); puts csv.fileno end
4
 => nil 

您可以更改该path =行以仅设置文件名(而不是打开它进行写入),然后update_report打开文件名进行读取。当你给它一个空的、已经覆盖的、为写入而打开的文件句柄时,我还没有深入研究 Paperclip 的作用,但是改变这个流程可能会解决这个问题。

或者,您可以在内存中执行此操作:将 CSV 生成为字符串并将其作为 StringIO 提供给 Paperclip。(Paperclip 支持某些非文件对象,包括 StringIO,使用例如Paperclip::StringioAdapter。)尝试类似:

# returns a CSV as a string
def compile_csv(items)
  CSV.generate do |csv|
     # ...
  end
end

def update_report(report_id, csv)
  report = Report.find(report_id)
  report.update_attributes(csv: StringIO.new(csv), status: true)
  report.save!
end
于 2012-09-09T05:19:00.640 回答