31

我继承了一个 Rails 2.2.2 应用程序,该应用程序将用户上传的图像存储在 Amazon S3 上。基于 attachment_fu 的Photo模型提供了rotate一种用于open-uri从 S3 和 MiniMagick 检索图像以执行旋转的方法。

rotate方法包含这一行来检索与 MiniMagick 一起使用的图像:

temp_image = MiniMagick::Image.from_file(open(self.public_filename).path)

self.public_filename返回类似的东西

http://s3.amazonaws.com/bucketname/photos/98/photo.jpg

检索图像并旋转它在生产和开发中运行的应用程序中工作得很好。但是,单元测试失败了

TypeError: can't convert nil into String
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `initialize'
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `open'
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `from_file'

原因是在单元测试的上下文中调用模型方法时,open(self.public_filename)正在返回一个StringIO包含图像数据的对象。这个对象的path方法返回nilMiniMagick::Image.from_file炸毁。

当从 调用这个相同的模型方法时PhotosControlleropen(self.public_filename)返回一个FileIO与名为 eg 的文件绑定的实例,/tmp/open-uri7378-0该文件包含图像数据。

想着一定是测试和开发环境的差异,我在开发环境下启动了控制台。但就像在单元测试中一样,open('http://...')返回 a StringIO而不是a FileIO

我已经通过 open-uri 和所有相关的特定于应用程序的代码追踪了我的方式,并且找不到任何差异的原因。

4

3 回答 3

74

open-uri 库使用一个常量来设置 StringIO 对象的 10KB 大小限制。

> OpenURI::Buffer::StringMax
=> 10240 

您可以将此设置更改为 0 以防止 open-uri 创建 StringIO 对象。相反,这将强制它始终生成一个临时文件。

只需将其放入初始化程序中:

# Don't allow downloaded files to be created as StringIO. Force a tempfile to be created.
require 'open-uri'
OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
OpenURI::Buffer.const_set 'StringMax', 0

您不能直接设置常量。您需要实际删除常量,然后再次设置它(如上),否则您会收到警告:

warning: already initialized constant StringMax

2012 年 12 月 18 日更新:Rails 3 默认不需要 OpenURI,因此您需要require 'open-uri'初始化程序的顶部添加。我更新了上面的代码以反映这种变化。

于 2011-07-09T01:05:35.037 回答
30

负责这个的代码在 open-uri 的 Buffer 类中。它首先创建一个 StringIO 对象,并且仅在数据超过一定大小(10 KB)时在本地文件系统中创建一个实际的临时文件。

我假设您的测试加载的任何数据都足够小,可以保存在 StringIO 中,并且您在实际应用程序中使用的图像足够大,可以保证使用 TempFile。解决方案是使用两个类共有的方法,特别是 read 方法,使用 MiniMagick::Image#from_blob:

temp_image = MiniMagick::Image.from_blob(open(self.public_filename, &:read))
于 2009-03-29T10:14:34.827 回答
0

现在OpenURI::Buffer::StringMax可以直接设置:

require 'open-uri'
OpenURI::Buffer::StringMax = 0

但有一个警告:

warning: already initialized constant OpenURI::Buffer::StringMax
于 2020-07-09T19:32:02.413 回答