使用流媒体转发sinatra中的文件下载



我制作了一个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上缓存文件。

有什么想法吗?

要以应用程序为代理的流媒体方式实现这一点,您需要在下载区块时发送客户端区块。这不是ruby/Net::HTTP的默认行为,但这是可能的。

来自ruby文档:

默认情况下,Net::HTTP将整个响应读取到内存中。如果您正在处理大型文件或希望实现进度条,您可以将正文直接流式传输到IO。

不过,可以通过read_body进行流式传输。

Net::HTTP流式响应体

文档中的示例用法:

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的流媒体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这样的前端可能会影响流式响应的缓冲/分块。

最新更新