【问题标题】:How to stream response rails app on heroku如何在heroku上流式响应rails应用程序
【发布时间】:2012-12-01 19:08:18
【问题描述】:

我有一个在 heroku 上运行的 rails 3.1 应用程序。 我需要为用户提供下载 csv 数据的能力。 我正在尝试流式传输数据,但它是一次性发送的。 对于较大的请求,哪个会超时。

heroku 网站上有很多关于流和分块的讨论 但据我所知,thin 会收集所有数据并一次性发送。 如何让它工作?

我必须添加一些中间件吗?例如独角兽 代码流在 mongrel 上运行良好。

【问题讨论】:

  • 看起来我确实需要使用独角兽,但我必须增加超时时间才能使进程不被终止。我不认为这是一个解决方案。

标签: heroku http-chunked


【解决方案1】:

我很确定你只需要添加

stream

到控制器的顶部。

更多关于 HTTP 流的信息可以在 RailsCasts 上找到:http://railscasts.com/episodes/266-http-streaming

【讨论】:

  • 谢谢,但流已被弃用,它所做的只是设置标题并使用 Rack::Chunked,我一直在这样做。看来我必须使用独角兽。虽然现在我收到错误“传输已关闭,但仍有未完成的读取数据”
【解决方案2】:

这个问题确实很老,但由于 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,以便分批获取数据库。

【讨论】:

    猜你喜欢
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多