How to upload remote file from url with ActiveStorage Rails
Today I've Learned postclass Medium < ActiveRecord::Base
has_one_attached :image
end
Attach remote file
with ‘require open-uri’
require 'open-uri'
file = open('https://meme.eq8.eu/noidea.jpg')
medium = Medium.last
medium.image.attach(io: file, filename: 'some-image.jpg')
# or
medium.image.attach(io: file, filename: 'some-image.jpg', content_type: 'image/jpg')
Or without require
require 'uri'
file = URI.open('https://meme.eq8.eu/noidea.jpg')
medium = Medium.last
medium.image.attach(io: file, filename: 'some-image.jpg')
# or
medium.image.attach(io: file, filename: 'some-image.jpg', content_type: 'image/jpg')
If you want to get filename from url
require 'uri'
uri = URI.parse('https://meme.eq8.eu/noidea.jpg')
filename = File.basename(uri.path)
So put all that together
require 'uri'
url = 'https://meme.eq8.eu/noidea.jpg'
filename = File.basename(URI.parse(url).path)
file = URI.open('https://meme.eq8.eu/noidea.jpg')
medium = Medium.last
medium.image.attach(io: file, filename: filename, content_type: 'image/jpg')
to get the mimetype you can use
Rack::Mime.mime_type('.jpg')
from the".#{url.split('.').last}
Attach local file
medium = Medium.last
medium.image.attach(io: File.open("/tmp/some-image.jpg"), filename: "some-image.jpg", content_type: "image/jpg")
If you want to get filename and mime_type from the file:
filename = File.basename("/tmp/some-image.jpg") # => some-image.jpg
extension = File.extname("/tmp/some-image.jpg") # => .jpg
mime_type = Rack::Mime.mime_type(a) #=> "image/jpeg"
or with Pathname
pathname = Pathname.new('/tmp/some-image.jpg')
pathname.basename.to_s # => some-image.jpg
pathname.extname # => .jpg
Rack::Mime.mime_type(pathname.extname) # => "image/jpeg"
all together:
medium = Medium.last
pathname = Pathname.new('/tmp/some-image.jpg')
medium.image.attach(io: File.open(pathnname), filename: pathname.basename.to_s, content_type: Rack::Mime.mime_type(pathname.extname))
Attach local file as a uploaded medium
Sometimes you want the file in a console to appear as it was uploaded via a Controller.
For example if you are writing a db:seed where you attach local file you may want to reuse bit of functionality that uses bit of controller logic:
medium.image = params.permit(:image)
so Here you wont be able to do medium.image.attach(io: File.open('...'), content_type: '...')
Solution:
# app/controller/media_controller.rb
# ...
def create
# ...
Medium.upload_via_controller(params[:image])
# ...
end
end
# app/models/medium.rb
class Medium < ActiveRecord::Base
has_one_attached :image
def self.upload_via_controller(file_from_controller)
medium = Medium.new
medium.image = file_from_controller
# ...some other logic like set default order
medium.save
end
end
# lib/tasks/create_dummy_data.rake
module MyRakeHelper
extend ActionDispatch::TestProcess
def self.create_dummy_medium
img = Rails.root.join('db/data/dummy-work.jpg')
img = dummy_file_uplod(img.to_s, 'image/jpg')
## ...or:
# img = dummy_file_uplod(img.to_s, Rack::Mime.mime_type(img.extname))
Medium.upload_via_controller(img) # we want to reuse existing logic in our raketask
end
def self.dummy_file_uplod(file_fixture_path, type)
Rack::Test::UploadedFile.new(file_fixture_path, type)
end
end
task create_dummy_images: :environment do
MyRakeHelper.create_dummy_medium
end
Note: For tests you could use:
# spec/my_spec.rb
# require 'action_dispatch/testing/test_proces' # may be needed
module UploadHelper
extend ActionDispatch::TestProcess::FixtureFile # rails 6
# extend ActionDispatch::TestProcess # order Rails like rails 3.2
def upload(img)
fixture_file_upload(img.to_s, Rack::Mime.mime_type(img.extname))
end
end
it do
img = UploadHelper.upload( Rails.root.join('spec/fixtures/myimg.jpg) )
medium = Medium.create(image: img)
# ...
end
- https://api.rubyonrails.org/classes/ActionDispatch/TestProcess/FixtureFile.html
- https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/actionpack/lib/action_dispatch/testing/test_process.rb#L19
Sources
- https://edgeguides.rubyonrails.org/active_storage_overview.html#attaching-file-io-objects
- https://blog.eq8.eu/til/factory-bot-trait-for-active-storange-has_attached.html
Related articles
Entire blog website and all the articles can be forked from this Github Repo