How to save an image from a url with rails active storage?
Just found the answer to my own question. My first instinct was pretty close...
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
Update: please note comment below, "you have to be careful not to pass user input to open, it can execute arbitrary code, e.g. open("|date")"
Like said in the comments, the use of open
or URI.open
is very dangerous, since it can not only access files but also process invocation by prefixing a pipe symbol (e.g. open("| ls")
).
Kernel#open
andURI.open
enable not only file access but also process invocation by prefixing a pipe symbol (e.g.,open("| ls")
). So, it may lead to a serious security risk by using variable input to the argument ofKernel#open
andURI.open
. It would be better to useFile.open
,IO.popen
orURI.parse#open
explicitly.
Extracted from the Rubocop documentation: https://docs.rubocop.org/rubocop/1.8/cops_security.html#securityopen
So, a safer solution would be:
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
The simplest way to do this without having to enter filename explicitly is:
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)
This automatically saves the avatar against that particular user object.
In case you are using a remote service like S3 the URL can be retrieved by:
user.avatar.service_url
using the down
gem to avoid the security issues of using open-uri:
image = Down.download(image_url)
user.image.attach(io: image, filename: "image.jpg")