Ruby 文件 IO:无法打开 url 作为文件对象

Ruby File IO: Can't open url as File object

我的代码中有一个函数,它接受一个表示图像 url 的字符串,并从该字符串创建一个 File 对象,以附加到推文。这似乎在大约 90% 的时间都有效,但偶尔会失败。

require 'open-uri'
attachment_url = "https://s3.amazonaws.com/FirmPlay/photos/images/000/002/443/medium/applying_too_many_jobs_-_daniel.jpg?1448392757"
image = File.new(open(attachment_url))

如果我运行上面的代码它returnsTypeError: no implicit conversion of StringIO into String。如果我将 open(attachment_url) 更改为 open(attachment_url).read,我会得到 ArgumentError: string contains null byte。我也试过像这样从文件中删除空字节,但这也没有什么区别。

image = File.new(open(attachment_url).read.gsub("\u0000", ''))

现在,如果我用不同的图像尝试原始代码,例如下面的图像,它工作正常。它 returns 一个 File 预期的对象:

attachment_url = "https://s3.amazonaws.com/FirmPlay/photos/images/000/002/157/medium/mike_4.jpg"

我想这可能与原始 url 中的参数有关,所以我将其删除,但没有任何区别。如果我打开 Chrome 中的图像,它们似乎没问题。

我不确定我在这里遗漏了什么。我该如何解决这个问题?

谢谢!

更新

这是我的应用程序中的工作代码:

filename = self.attachment_url.split(/[\/]/)[-1].split('?')[0]
stream = open(self.attachment_url)
image = File.open(filename, 'w+b') do |file|
    stream.respond_to?(:read) ? IO.copy_stream(stream, file) : file.write(stream)
    open(file)
end

Jordan 的答案有效,除了调用 File.new returns 一个空的 File 对象,而 File.open returns 一个 File 对象包含图片数据来自 stream.

你得到 TypeError: no implicit conversion of StringIO into String 的原因是 open 有时 returns 一个 String 对象,有时 returns 一个 StringIO 对象,这是不幸和令人困惑的。它的作用取决于文件的大小。有关详细信息,请参阅此答案:open-uri returning ASCII-8BIT from webpage encoded in iso-8859(尽管我不建议使用其中提到的确保编码 gem,因为它自 2010 年以来一直没有更新,并且 Ruby 具有重要意义从那时起与编码相关的变化。)

你得到 ArgumentError: string contains null byte 的原因是你试图将图像数据作为第一个参数传递给 File.new:

image = File.new(open(attachment_url))

File.new 的第一个参数应该是文件名,大多数系统的文件名中不允许空字节。试试这个:

image_data = open(attachment_url)

filename = 'some-filename.jpg'

File.new(filename, 'wb') do |file|
  if image_data.respond_to?(:read)
    IO.copy_stream(image_data, file)
  else
    file.write(image_data)
  end
end

以上打开文件(如果文件不存在就创建它;'wb'中的b告诉Ruby你要写入二进制数据),然后如果它是 StreamIO 对象,则使用 IO.copy_stream 将来自 image_data 的数据写入其中,否则使用 File#write,然后再次关闭文件。

如果您使用 Paperclip,他们有一种方法可以复制到磁盘。

def raw_image_data
  attachment.copy_to_local_file.read
end

将附件更改为您当然使用过的变量。