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

问题描述 投票:0回答:5

我正在使用 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

预先感谢您的任何建议。

ruby-on-rails rails-activestorage
5个回答
51
投票

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

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")”


32
投票

就像评论中所说,使用

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

Kernel#open
URI.open
不仅支持文件访问,还支持通过前缀管道符号(例如
open("| ls")
)进行进程调用。因此,使用变量输入到
Kernel#open
URI.open
的参数可能会导致严重的安全风险。最好明确使用
File.open
IO.popen
URI.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

7
投票

使用

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

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

5
投票

无需显式输入文件名即可执行此操作的最简单方法是:

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

0
投票
require 'open-uri'

class User < ApplicationRecord
  has_one_attached :avatar

  def attach_avatar_from_url
    file = URI.open(url)
    avatar.attach(io: file, filename: File.basename(URI.parse(url).path))
  end

end

user = User.find(1)
user.attach_avatar_from_url('https://example.com/image.jpg')
© www.soinside.com 2019 - 2024. All rights reserved.