6

我想通过 Sinatra 应用程序代理远程文件。这需要将带有标头的 HTTP 响应从远程源流式传输回客户端,但我无法弄清楚如何在Net::HTTP#get_response.

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

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
4

1 回答 1

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

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

希望能帮助到你。

于 2012-09-18T18:10:20.530 回答