2

我想知道是否有人知道如何在创建文件的同时流式传输文件下载。

我正在生成一个巨大的 CSV 导出文件,截至目前,创建文件需要几分钟时间。一旦它创建浏览器然后下载文件。

我想更改它,以便浏览器在创建文件时开始下载文件。看着这个进度条用户会更愿意等待。尽管它会告诉我“剩余时间未知”,但我不太可能不耐烦,因为我知道数据正在稳步下载。

注意:我使用的是 Rails 版本 3.0.9

这是我的代码:

def users_export
  File.new("users_export.csv", "w")                 # creates new file to write to
  @todays_date = Time.now.strftime("%m-%d-%Y")
  @outfile = @todays_date + ".csv"

  @users = User.select('id, login, email, last_login, created_at, updated_at')

  FasterCSV.open("users_export.csv", "w+") do |csv|
    csv << [ @todays_date ]

    csv << [ "id","login","email","last_login", "created_at", "updated_at" ]
    @users.find_each(:batch_size => 100 ) do |u|
      csv << [ u.id, u.login, u.email, u.last_login, u.created_at, u.updated_at ]
    end
  end

  send_file "users_export.csv",
    :type => 'text/csv; charset=iso-8859-1; header=present',
    :disposition => "attachment; filename=#{@outfile}",
    :stream => true,
end
4

1 回答 1

1

几周前,我寻求这个问题的答案。我认为如果数据被流回客户端,那么 Heroku 可能不会在 30 秒后使我长时间运行的 API 调用超时。我什至找到了一个看起来很有希望的答案:

format.xml do
  self.response_body =
    lambda { |response, output|
      output.write("<?xml version='1.0' encoding='UTF-8' ?>")
      output.write("<results type='array' count='#{@report.count}'>")
      @report.each do |result|
        output.write("""
          <result>
            <element-1>Data-1</element-1>
            <element-2>Data-2</element-2>
            <element-n>Data-N</element-n>
          </result>
        """)
      end
      output.write("</results>")
    }
  end

这个想法是 response_body lambda 可以直接访问返回到客户端的输出缓冲区。然而,在实践中,Rack 对应该发回哪些数据以及何时发回有自己的想法。此外,这种作为 lambda 模式的 response_body 在较新版本的 rails 中已被弃用,我认为在 3.2 中完全放弃了支持。你可以在中间件堆栈中弄脏你的手,并将这个输出写成Rails Metal但是......

如果我可以这么大胆,我强烈建议将这项工作重构为后台工作。好处很多:

  • 您的用户不必只是坐下来等待下载。他们可以请求文件,然后浏览到您网站的其他更令人兴奋的部分。

  • 文件生成和下载将更加强大,例如,如果用户在当前设置下下载的第三分钟失去互联网连接,即使是短暂的,他们将失去所有时间并需要重新开始。如果文件是在您网站的后台生成的,那么只要开始工作,他们就只需要互联网。

  • 如果后台作业生成文件并且您在应用程序的页面上提供生成文件的链接,它将减少前端进程的负载,并且可能会减少您网站的负载。有可能一个文件生成可以提供多个下载。

  • 由于几乎所有 Rails Web 服务器都是开箱即用的单线程和同步的,因此每次用户请求时,您都会有一个完整的应用服务器进程与这个文件下载相关联。这使用户很容易意外地对您的站点进行 DoS 攻击。

  • 您可以将后台生成的文件发送到诸如 S3 之类的 CDN,并且可能会在您的用户看到的下载速度上获得性能提升。

  • 后台进程完成后,您可以通过电子邮件通知用户,这样他们甚至不必在他们启动文件生成的计算机上就可以知道它已完成。

  • 在应用程序中拥有后台作业系统后,您会发现它的更多用途,例如发送电子邮件或更新搜索索引。

抱歉,这并不能真正回答您的原始问题。但我坚信这是一个更好的整体解决方案。

于 2012-07-03T21:25:27.210 回答