【问题标题】:Ruby File IO: Can't open url as File objectRuby File IO:无法将 url 作为文件对象打开
【发布时间】:2015-12-08 16:37:18
【问题描述】:

我的代码中有一个函数,它接受一个表示图像 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))

如果我运行上面的代码,它会返回TypeError: 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", ''))

现在,如果我尝试使用不同图像的原始代码,如下图所示,它可以正常工作。它按预期返回一个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 返回一个空的 File 对象,而 File.open 返回一个包含来自 stream 的图像数据的 File 对象。

【问题讨论】:

  • 从您的描述中不清楚:是不是这种情况,对于某些 jpg 文件 总是 失败,而对于某些其他文件 总是 成功,或者是不是对于给定的 URI,它有时会失败有时会成功?
  • 对于某些文件总是失败,但对于其他文件永远不会。例如,第一个文件链接总是失败,而第二个文件链接总是成功。我怀疑文件本身可能有问题,所以我想知道如何检测和规避它。
  • 如果它是二进制文件(如 JPEG),您不想去除空字节。这些是图像数据的一部分。

标签: ruby file open-uri


【解决方案1】:

您得到TypeError: no implicit conversion of StringIO into String 的原因是open 有时会返回一个String 对象,有时会返回一个StringIO 对象,这很不幸且令人困惑。它的作用取决于文件的大小。有关更多信息,请参阅此答案:open-uri returning ASCII-8BIT from webpage encoded in iso-8859(尽管我不建议使用其中提到的 ensure-encoding 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 你要写入二进制数据),然后使用 image_data 将数据写入它IO.copy_stream 如果它是 StreamIO 对象或 File#write 否则,然后再次关闭文件。

【讨论】:

  • 感谢您的解决方案,也感谢您阐明两种写入文件的方法之间的区别。我觉得我现在对 Ruby 中的 File/IO 有了更好的理解。
  • 好吧,我以为它可以工作,但是一旦我将它插入我的代码,它又坏了。现在我得到IOError: not opened for reading 与每张图片一致。
  • 另外,当我调用image.size 时返回0
  • 如果没有完整的错误消息和行号,很难知道错误来自哪里。至于image.size返回0我不知道,因为我没有在我的代码中使用变量名image
  • 我刚刚将我的代码添加到我的帖子中。如您所见,我将File 对象保存到image。当我打电话给tweet = company.twitter_client.update_with_media(JSON.parse(self.posts)['twitter'], image) 时,我从/Users/ACIDSTEALTH/.gem/ruby/2.0.0/gems/multipart-post-2.0.0/lib/composite_io.rb:102:in read'` 收到错误IOError: not opened for reading
【解决方案2】:

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

def raw_image_data
  attachment.copy_to_local_file.read
end

当然,将附件更改为您使用的任何变量。

【讨论】:

    猜你喜欢
    • 2018-11-30
    • 2012-03-26
    • 2011-12-23
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 2013-05-05
    • 2015-03-25
    • 1970-01-01
    相关资源
    最近更新 更多