Rails: open()返回StringIO而不是Tempfile

6
我有两个指向不同图片的有效链接。 当我在第一个链接上运行open()时,它会返回一个类型为Tempfile的对象(这是fog gem上传图片到AWS所期望的对象)。 当我在第二个链接上运行open()时,它返回一个类型为StringIO的对象(导致fog gem崩溃)。
为什么open()在第二个链接上没有返回Tempfile? 此外,是否可以强制open()始终返回Tempfile?
来自我的Rails Console:
2.2.1 :011 > url1
 => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/c0.0.448.448/10298878_10103685138839040_6456490261359194847_n.jpg?oh=e2951e1a1b0a04fc2b9c0a0b0b191ebc&oe=56195EE3&__gda__=1443959086_417127efe9c89652ec44058c360ee6de" 
2.2.1 :012 > url2
 => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfa1/v/t1.0-1/c0.17.200.200/1920047_10153890268465074_1858953512_n.jpg?oh=5f4cdf53d3e59b8ce4702618b3ac6ce3&oe=5610ADC5&__gda__=1444367255_396d6fdc0bdc158e4c2e3127e86878f9" 
2.2.1 :013 > t1 = open(url1)
 => #<Tempfile:/var/folders/58/lpjz5b0n3yj44vn9bmbrv5180000gn/T/open-uri20150720-24696-1y0kvtd> 
2.2.1 :014 > t2 = open(url2)
 => #<StringIO:0x007fba9c20ae78 @base_uri=#<URI::HTTPS https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfa1/v/t1.0-1/c0.17.200.200/1920047_10153890268465074_1858953512_n.jpg?oh=5f4cdf53d3e59b8ce4702618b3ac6ce3&oe=5610ADC5&__gda__=1444367255_396d6fdc0bdc158e4c2e3127e86878f9>, @meta={"last-modified"=>"Tue, 25 Feb 2014 19:47:06 GMT", "content-type"=>"image/jpeg", "timing-allow-origin"=>"*", "access-control-allow-origin"=>"*", "content-length"=>"7564", "cache-control"=>"no-transform, max-age=1209600", "expires"=>"Mon, 03 Aug 2015 22:01:40 GMT", "date"=>"Mon, 20 Jul 2015 22:01:40 GMT", "connection"=>"keep-alive"}, @metas={"last-modified"=>["Tue, 25 Feb 2014 19:47:06 GMT"], "content-type"=>["image/jpeg"], "timing-allow-origin"=>["*"], "access-control-allow-origin"=>["*"], "content-length"=>["7564"], "cache-control"=>["no-transform, max-age=1209600"], "expires"=>["Mon, 03 Aug 2015 22:01:40 GMT"], "date"=>["Mon, 20 Jul 2015 22:01:40 GMT"], "connection"=>["keep-alive"]}, @status=["200", "OK"]>

这是我使用fog的方式:
tempfile = open(params["avatar"])
user.avatar.store!(tempfile)
3个回答

14

我假设您正在使用Ruby内置的open-uri库,它允许您使用open()下载URL。

在这种情况下,Ruby只有返回一个IO对象的义务。不能保证它会是一个文件。我的猜测是基于内存消耗,如果下载内容很大,Ruby将其放入文件中以节省内存;否则将其保存在内存中并使用StringIO

作为解决方法,您可以编写一个方法,如果流尚未下载到文件中,则将其写入临时文件:

def download_to_file(uri)
  stream = open(uri, "rb")
  return stream if stream.respond_to?(:path) # Already file-like

  Tempfile.new.tap do |file|
    file.binmode
    IO.copy_stream(stream, file)
    stream.close
    file.rewind
  end
end

如果你正在寻找一个类似的具有完整功能的 gem,请看一下 "down":https://github.com/janko-m/down


谢谢!我在我的回答中做了类似的事情(但更简单)。 - etayluz
所以,在解决类似问题时,我发现 file.close 应该改为 stream.close,并且在返回文件之前最好先执行 file.rewind。不过我的 Ruby 还不太熟练,所以没有修改你的答案 ;) - sg3s
@sg3s 好建议,谢谢!我已经更新了我的回答。 - Matt Brictson

8

开放的uri库对于StringIO或Tempfile有10K大小限制。

我的建议是改为常量OpenURI::Buffer::StringMax,这是用于设置默认值的open uri缓冲区大小。

在你的初始化器中可以这样做:

require 'open-uri'

OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
OpenURI::Buffer.const_set 'StringMax', 0

0

这并没有回答我的问题,但它提供了一个使用httparty gem的可行替代方案:

require "httparty"

File.open("file.jpg", "wb") do |tempfile| 
  tempfile.write HTTParty.get(params["avatar"]).parsed_response
  user.avatar.store!(tempfile)
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接