我创建了一个ruby / sinatra网站,我需要让用户下载文件。 此文件不是本地托管的,它托管在远程API上。最终用户不得看到文件的真正来源。
get "/files/:elementKey/masterfile" do
content_type "application/octet-stream"
loadMasterfile(params[:elementKey])
end
使用loadMasterfile:
http = Net::HTTP.new(plainURI,443)
http.use_ssl = true;
http.start do |http|
req = Net::HTTP::Get.new(resource, {"User-Agent" =>"API downloader"})
req.basic_auth(user.keytechUserName, user.keytechPassword)
response = http.request(req)
# return this as a file attachment
attachment( response["X-Filename"]) #Use the sinatra helper to set this as filename
response.body << This lets sinatra download the file and then forward the whole content to the browser
end
此代码有效,但是: 该文件首先下载到ruby / sinatra,然后转发到浏览器。 用户必须等到下载开始 - 浏览器似乎冻结。
是否有解决方案从远程API开始下载并在一个流程中转发内容?
我没有发现任何相关内容或仅找到本地文件下载的解决方案,但我必须从远程API下载文件。
我也无法在本地或亚马逊AWS上缓存文件。
任何想法?
答案 0 :(得分:1)
要以您的应用为代理的流媒体方式实现此目的,您需要在下载块时发送客户端块。这不是ruby / Net::HTTP
的默认行为,但它是可能的。
来自ruby docs:
默认情况下,Net :: HTTP会将整个响应读入内存。如果您正在处理大型文件或希望实现进度条,则可以将主体直接流式传输到IO。
但可以通过read_body
进行流式传输。
Net::HTTP
Streaming Response Bodies
来自文档的示例用法:
uri = URI('http://example.com/large_file')
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
http.request request do |response|
open 'large_file', 'w' do |io|
response.read_body do |chunk|
io.write chunk
end
end
end
end
此文档中的示例将流数据写入文件,但您可以将其替换为对响应流的写入。结合Sinatra's streaming api,代码可能如下所示:
get "/files/:elementKey/masterfile" do
content_type "application/octet-stream"
stream do |out|
loadMasterfile(params[:elementKey]) do |chunk|
out << chunk
end
end
end
def loadMasterfile(resource, &block)
http = Net::HTTP.new(plainURI, 443)
http.use_ssl = true;
http.start do |http|
req = Net::HTTP::Get.new(resource, {"User-Agent" =>"API downloader"})
req.basic_auth(user.keytechUserName, user.keytechPassword)
http.request(req) do |origin_repsonse|
origin_repsonse.read_body(&block)
end
end
end
我不确定你是如何设置文件名的。您还希望在网络呼叫和流关闭中适当地处理错误。另请注意,像nginx这样的前端会影响流式响应的缓冲/分块。