创建文件时进行流式下载

时间:2012-07-03 20:35:45

标签: ruby-on-rails

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

我正在生成巨大的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

1 个答案:

答案 0 :(得分: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攻击。

  • 您可以将后台生成的文件发送到CDN(如S3),或者可以提高用户看到的下载速度。

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

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

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