如何使用 Rails 主动存储从 url 保存图像?

Posted

技术标签:

【中文标题】如何使用 Rails 主动存储从 url 保存图像?【英文标题】:How to save an image from a url with rails active storage? 【发布时间】:2018-12-04 07:06:22 【问题描述】:

我希望使用 rails 5.2 主动存储来保存位于另一个 http web 服务器上的文件(在本例中为图像)。

我有一个带有源 url 字符串参数的对象。然后在 before_save 上,我想抓取远程图像并保存它。

示例:图片的 URL http://www.example.com/image.jpg。

require 'open-uri'

class User < ApplicationRecord
  has_one_attached :avatar
  before_save :grab_image

  def grab_image
    #this indicates what I want to do but doesn't work
    downloaded_image = open("http://www.example.com/image.jpg")
    self.avatar.attach(downloaded_image)
  end

end

提前感谢您的任何建议。

【问题讨论】:

【参考方案1】:

刚刚找到我自己问题的答案。我的第一直觉非常接近......

require 'open-uri'

class User < ApplicationRecord
  has_one_attached :avatar
  before_save :grab_image

  def grab_image
    downloaded_image = open("http://www.example.com/image.jpg")
    self.avatar.attach(io: downloaded_image  , filename: "foo.jpg")
  end

end

更新:请注意下面的评论,“您必须注意不要将用户输入打开,它可以执行任意代码,例如 open("|date")”

【讨论】:

我尝试使用此代码,但我无法存储到达 URL 的图像和图像,因为我收到此错误:ActiveSupport::MessageVerifier::InvalidSignature,但是当我从我的记录中开车到显示视图,实际上存储了图像。我从控制器的参数中获取了 URL,并使用此方法与模型连接(唯一的区别是我在 () 之间发送我得到的参数到控制器。有人知道我为什么会遇到这个问题吗? 在查看了关于 activestorage 的 github 问题后来到这里。至少可以在开发中确认这对我来说非常有用,适用于不同大小的多种文件类型。 你必须小心不要将用户输入传递给open,它可以执行任意代码,例如open("|date") 注意:您尝试附加的文件通常需要权限标头才能发出请求;如果您像我一样来这里寻找如何附加/打开带有权限标题的文件,然后将此答案与以下线程结合起来,您将得到完美的答案。 ***.com/questions/7478841/…【参考方案2】:

使用down gem 来避免使用 open-uri 的安全问题:

image = Down.download(image_url)
user.image.attach(io: image, filename: "image.jpg")

【讨论】:

【参考方案3】:

正如 cmets 中所说,使用openURI.open 是非常危险的,因为它不仅可以访问文件,还可以通过前缀管道符号(例如open("| ls"))来处理调用。

Kernel#openURI.open 不仅可以访问文件,还可以通过前缀管道符号(例如,open("| ls"))进行进程调用。因此,在Kernel#openURI.open 的参数中使用变量输入可能会导致严重的安全风险。最好明确使用File.openIO.popenURI.parse#open

摘自 Rubocop 文档:https://docs.rubocop.org/rubocop/1.8/cops_security.html#securityopen

因此,更安全的解决方案是:

class User < ApplicationRecord
  has_one_attached :avatar
  before_save :grab_image

  def grab_image
    downloaded_image = URI.parse("http://www.example.com/image.jpg").open
    avatar.attach(io: downloaded_image, filename: "foo.jpg")
  end
end

【讨论】:

【参考方案4】:

无需显式输入文件名的最简单方法是:

url = URI.parse("https://your-url.com/abc.mp3")
filename = File.basename(url.path)
file = URI.open(url)
user = User.first
user.avatar.attach(io: file, filename: filename)

这会针对该特定用户对象自动保存头像。

如果您使用像 S3 这样的远程服务,则可以通过以下方式检索 URL:

user.avatar.service_url

【讨论】:

请注意:DEPRECATION WARNING: service_url is deprecated and will be removed from Rails 6.2 (use url instead)

以上是关于如何使用 Rails 主动存储从 url 保存图像?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不保存原始图像的情况下保存图像 URL 的裁剪图像? (在 Rails 中使用 Paperclip 或其他插件)

如何获取模型中图像变体的 url(在控制器/视图之外)?主动存储

如何从 Rails Active Storage 中的 url 附加图像

如何以编程方式保存来自 URL 的图像?

Rails:如何从URL中读取图像列表

将图像保存在 iOS 相册(我的文件夹)中并存储图像 url