class 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

Sources