我想知道是否有人知道如何在同时创建文件下载时传输文件。
我正在生成巨大的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
答案 0 :(得分:1)
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),或者可以提高用户看到的下载速度。
当后台处理完成后,您可以通过电子邮件通知用户,这样他们甚至不必在他们启动文件生成的计算机上,以便知道它已经完成。
在您的应用程序中拥有后台作业系统后,您会发现它有更多用途,例如发送电子邮件或更新搜索索引。
很抱歉,这并不能真正回答您的原始问题。但我坚信这是一个更好的整体解决方案。