Ruby on rails 创建文件时进行流式下载

Ruby on rails 创建文件时进行流式下载,ruby-on-rails,Ruby On Rails,我想知道是否有人知道在创建文件的同时如何流式下载文件 我正在生成一个巨大的CSV导出,现在创建文件需要几分钟的时间。创建文件后,浏览器将下载该文件 我想更改此设置,以便浏览器在创建文件时开始下载文件。查看此进度条,用户将更愿意等待。即使它会告诉我还有一个“未知时间”,我也不太可能不耐烦,因为我知道数据正在稳步下载 注意:我使用的是Rails版本3.0.9 这是我的密码: def users_export File.new("users_export.csv", "w")

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

我正在生成一个巨大的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
def用户\u导出
新建(“users_export.csv”,“w”)#创建要写入的新文件
@todays\u date=Time.now.strftime(“%m-%d-%Y”)
@outfile=@todays\u date+“.csv”
@users=User.select('id,login,email,last\u login,created\u at,updated\u at'))
FasterCSV.open(“users_export.csv”,“w+”)do|csv|
csv“附件;文件名=#{@outfile}”,
:stream=>true,
结束

几周前,我曾寻求这个问题的答案。我认为,如果数据被流式传输回客户端,那么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
format.xml do
自我反应=
lambda{|响应,输出|
输出。写入(“”)
输出。写入(“”)
@报告每个do的结果|
输出。写入(“”)
数据-1
数据-2
数据-N
""")
结束
输出。写入(“”)
}
结束
其思想是,响应体lambda将直接访问返回到客户端的输出缓冲区。然而,在实践中,Rack对什么样的数据应该被发回以及何时发回有自己的想法。此外,在更新版本的rails中,这种响应作为lambda模式被弃用,我认为在3.2中支持被彻底放弃。您可以在中间件堆栈中弄脏您的双手,并将此输出作为一个文件写入,但是

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

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

  • 文件生成和下载将更加健壮,例如,如果用户失去互联网连接,即使是在当前设置下下载的第三分钟,他们也会失去所有时间,需要重新开始。如果文件是在你网站的后台生成的,他们只需要在开始工作之前上网

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

  • 由于实际上所有Rails web服务器都是单线程的,并且是同步的,所以每次用户请求下载一个文件时,您都会有一个完整的应用程序服务器进程。这使得用户很容易在您的站点上意外执行DoS攻击

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

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

  • 一旦你的应用程序中有了一个后台工作系统,你就会发现它有更多的用途,比如发送电子邮件或更新搜索索引


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

几周前,我寻求了这个问题的答案。我认为,如果数据被流式传输回客户端,那么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
format.xml do
自我反应=
lambda{|响应,输出|
输出。写入(“”)
输出。写入(“”)
@报告每个do的结果|
输出。写入(“”)
数据-1
数据-2
数据-N
""")
结束
输出。写入(“”)
}
结束
其思想是,响应体lambda将直接访问返回到客户端的输出缓冲区。然而,在实践中,Rack对什么样的数据应该被发回以及何时发回有自己的想法。此外,在更新版本的rails中,这种响应作为lambda模式被弃用,我认为在3.2中支持被彻底放弃。您可以在中间件堆栈中弄脏您的双手,并将此输出作为一个文件写入,但是

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

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

  • 文件生成和下载将更加健壮,例如,如果用户失去互联网连接,即使是在当前设置下下载的第三分钟,他们也会失去所有时间,需要重新开始。如果文件是在你网站的后台生成的,他们只需要在开始工作之前上网

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