2012-09-11 33 views
5

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

例如,这将不设置响应头:

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 

回答

3

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

对此的一种可能的解决方法是在流回文件内容之前发出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 

它可能不是适合您的使用情况,因为额外的要求,但它应该是足够小,不会有太大的问题(延迟),并将其添加的好处,如果该请求在发回任何数据之前失败,您的应用程序可能会作出反应。

希望它有帮助。