带标题的Sinatra流式响应

时间:2012-09-11 00:21:55

标签: ruby sinatra

我想通过Sinatra应用程序代理远程文件。这需要将来自远程源头的HTTP响应流式传输回客户端,但我无法弄清楚如何在Net::HTTP#get_response提供的块内使用流式API时设置响应的头部。

例如,这不会设置响应标头:

get '/file' do
  stream do |out|
    uri = URI("http://manuals.info.apple.com/en/ipad_user_guide.pdf")
    Net::HTTP.get_response(uri) do |file|
      headers 'Content-Type' => file.header['Content-Type']

      file.read_body { |chunk| out << chunk }
    end
  end
end

这会导致错误:Net::HTTPOK#read_body called twice (IOError)

get '/file' do
  response = nil
  uri = URI("http://manuals.info.apple.com/en/ipad_user_guide.pdf")
  Net::HTTP.get_response(uri) do |file|
    headers 'Content-Type' => file.header['Content-Type']

    response = stream do |out|
      file.read_body { |chunk| out << chunk }
    end
  end
  response
end

1 个答案:

答案 0 :(得分:3)

我可能错了,但在考虑了一下这个问题之后,在我看来,当从stream帮助程序块中设置响应头时,这些头不会被应用到响应中,因为执行块实际上是 deferred 。因此,可能会对块进行评估,并在开始执行之前将响应头设置为

可能的解决方法是在回传文件内容之前发出HEAD请求。

例如:

get '/file' do
  uri = URI('http://manuals.info.apple.com/en/ipad_user_guide.pdf')

  # get only header data
  head = Net::HTTP.start(uri.host, uri.port) do |http|
    http.head(uri.request_uri)
  end

  # set headers accordingly (all that apply)
  headers 'Content-Type' => head['Content-Type']

  # stream back the contents
  stream do |out|
    Net::HTTP.get_response(uri) do |f| 
      f.read_body { |ch| out << ch }
    end
  end
end

由于附加请求,它可能不适合您的用例,但它应该足够小,不会出现太大的问题(延迟),并且它会增加您的应用程序可能在该请求失败时能够做出反应的好处在发回任何数据之前。

希望它有所帮助。