我有一个在 heroku 上运行的 rails 3.1 应用程序。我需要为用户提供下载 csv 数据的能力。我正在尝试流式传输数据,但它是一次性发送的。对于较大的请求,哪个会超时。
heroku 网站上有很多关于流和分块的讨论,但据我所知,thin 收集所有数据并一次性发送。我如何让它工作?
我必须添加一些中间件吗?例如 unicorn 与 mongrel 一起运行的代码流很好。
我有一个在 heroku 上运行的 rails 3.1 应用程序。我需要为用户提供下载 csv 数据的能力。我正在尝试流式传输数据,但它是一次性发送的。对于较大的请求,哪个会超时。
heroku 网站上有很多关于流和分块的讨论,但据我所知,thin 收集所有数据并一次性发送。我如何让它工作?
我必须添加一些中间件吗?例如 unicorn 与 mongrel 一起运行的代码流很好。
我很确定你只需要添加
stream
到控制器的顶部。
有关 HTTP 流的更多信息可以在 RailsCasts 上找到: http: //railscasts.com/episodes/266-http-streaming
这个问题确实很老,但由于 Heroku 响应中的 30 英寸限制,这个问题仍然很常见,所以我将添加一些关于我如何实现它的代码。在带有 Puma 服务器的 Heroku 上与 Rails 5.2 和 6.1 一起使用。
我正在使用 # send_stream方法(仅存在于边缘导轨,未来的导轨 7 中)所以我只是复制了它 + 手动设置 Last-Modified 标题。在 rails 关注中添加了 all 以重用它。
module Streameable
extend ActiveSupport::Concern
include ActionController::Live
def send_stream(filename:, disposition: 'attachment', type: nil)
response.headers['Content-Type'] =
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
'application/octet-stream'
response.headers['Content-Disposition'] =
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename) # for Rails 5, use content_disposition gem
# extra: needed for streaming correctly
response.headers['Last-Modified'] = Time.now.httpdate
yield response.stream
ensure
response.stream.close
end
end
class ExporterController < ApplicationController
include Streameable
def index
respond_to do |format|
format.html # index.html
format.js # index.js
format.csv do
send_stream(attachment_opts) do |stream|
stream.write "email_address,updated_at\n"
50.times.each do |i|
line = "user_#{i}@acme.com,#{Time.zone.now}\n"
stream.write line
puts line
sleep 1 # force slow response for testing respose > 30''
end
end
end
end
end
private
def attachment_opts
{
filename: "data_#{Time.zone.now.to_i}.csv",
disposition: 'attachment',
type: 'text/csv'
}
end
end
然后,如果您使用 curl 之类的东西,您将看到逐秒生成的输出。
$ curl -i http://localhost:3000/exporter.csv
重要的是编写代码以使用 Enumerable 模块使用 #each 迭代数据。哦,关于 ActiveRecord 的提示,使用#find_each以便分批获取数据库。