在创建文件时进行流式下载

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

我正在生成一个巨大的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 < 100 ) do |u| csv < 'text/csv; charset=iso-8859-1; header=present', :disposition => "attachment; filename=#{@outfile}", :stream => true, end 

几个星期前我找到了这个问题的答案。 我认为如果将数据流回客户端,那么Heroku不会在30秒后超时我的一个长时间运行的API调用。 我甚至找到了一个很有希望的答案:

 format.xml do self.response_body = lambda { |response, output| output.write("") output.write("") @report.each do |result| output.write("""  Data-1 Data-2 Data-N  """) end output.write("") } end 

想法是response_body lambda将直接访问返回客户端的输出缓冲区。 但是,在实践中,Rack对于应该发送什么数据以及何时发送数据有自己的想法。 此外,作为lambda模式的response_body在较新版本的rails中被弃用,我认为支持在3.2中完全放弃。 您可以在中间件堆栈中弄脏并将此输出写为Rails Metal但……

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

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

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

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

  • 由于几乎所有Rails Web服务器都是单线程和开箱即用的同步服务器,因此每次用户请求时,您都会在这一个文件下载中绑定整个应用服务器进程。 这使用户可以轻松地在您的网站上执行DoS攻击。

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

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

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

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