There are a few file upload gems out there but out of all of them we use Shrine because of it's flexibility and easy-to-extend nature.
Table of contents:
Shrine is a general purpose file uploader gem. As it has no direct dependency it can be used in both Rails and non-Rails projects. Shrine features a rich plugin system which makes it incredibly easy to change it's behavior to suit your needs. All this power is packaged in an easy-to-use form factor.
It is the youngest of all the popular file uploaders, but maintains an issue count of 0 and has fixed all the issues that the other gems have.
If you wish to read more about Shrine's design you can do so here.
Additionally there exists a demo application. It's not recommended to reference it as it's fairly simplistic and hides many configuration options.
To use shrine in a Rails project you need to do the following:
- Add it to your
Gemfile
along with it's image processing and cloud storage libraries
gem 'shrine'
# Only add this gem if you are going to use Amazon S3 as storage
gem 'aws-sdk', '~> 2.1'
# Only add these gems if you are going to handle image uploads
gem 'mini_magick'
gem 'image_processing'
gem 'fastimage'
- Run
bundle install
- Create a
shrine.rb
file in yourconfig/initializers
folder to configure Shrine
# config/initializers/shrine.rb
require 'shrine'
require 'shrine/storage/s3'
require 'shrine/storage/file_system'
# Load application secrets
secrets = Rails.application.secrets
# NOTE:
# The following code expects a secrets structure similar to the following
# and uses some Ruby 2.3 features
#
# development:
# aws:
# s3:
# access_key_id: 'ashkjdsahjas...'
# secret_access_key: 'ashkjdsahjas...'
# region: 'ashkjdsahjas...'
# bucket: 'ashkjdsahjas...'
# Load S3 credentials from secrets
s3_options = {
access_key_id: secrets.dig(:aws, 's3', 'access_key_id'),
secret_access_key: secrets.dig(:aws, 's3', 'secret_access_key'),
region: secrets.dig(:aws, 's3', 'region'),
bucket: secrets.dig(:aws, 's3', 'bucket')
}
# Load storage options
storages = {
cache: nil,
store: nil
}
# Set storage options to S3 if S3 credentials have been provided, else use the
# filesystem
storages.keys.each do |store|
storages[store] = if secrets.dig(:aws, :s3)
Shrine::Storage::S3.new(prefix: store, **s3_options)
else
Shrine::Storage::FileSystem.new(
'public',
prefix: "uploads/#{store}"
)
end
end
# Assign storage options
Shrine.storages = storages
# Load ORM integration
Shrine.plugin :activerecord
# Shrine.plugin :sequel # if you are using Sequel
- Require the image processing libraries in your
application.rb
file by prepending the following line
require 'image_processing/mini_magick'
- Create an uploader in your
app/uploaders
folder
# app/uploaders/avatar_uploader.rb
class AvatarUploader < Shrine
end
- Mount the uploader in your model
Shrine's uploaders are mounted on a model using the include
keyword.
You can specify to which attribute the uploader will get mounted by using the
square brackets method on the uploader class.
class User < ActiveRecord::Base
# The following line will mount an instance of AvatarUploader to the user's
# avatar attribute
include AvatarUploader[:avatar]
end
- Create the appropriate column on your model's table.
Shrine assumes that it should use a column bearing the name of the attribute it
was mounted on suffixed with _data
. E.g. if you mount an uploader on the
avatar
attribute of the User
model. Shrine will store information about the
uploaded file to the avatar_data
column of the users
table. The column's
type should either be text
, json
or jsonb
.
rails g migration add_avatar_data_to_users avatar_data:jsonb
- Create a file field in your view
= simple_form_for @user do |f|
.row
.col-xs-12
= f.input :avatar, as: :hidden, input_html: { value: @user.avatar_data }
= f.input :avatar, as: :file
If you use simple_form then add the following file to your app/inputs
folder
# app/inputs/shrine_file_input.rb
class ShrineFileInput < SimpleForm::Inputs::Base
def input(wrapper_options)
merged_input_options = merge_wrapper_options(
input_html_options, wrapper_options
)
raw_input_html(merged_input_options)
end
private
def raw_input_html(options)
%(
#{hidden_field}
#{file_field}
).html_safe
end
def data_attribute
"#{attribute_name}_data".to_sym
end
def hidden_field
@builder.hidden_field(
attribute_name,
value: object.send(data_attribute).to_json
)
end
def file_field
@builder.file_field(
attribute_name,
input_html_options
)
end
end
Then you can use it in your views as follows:
= simple_form_for @user do |f|
.row
.col-xs-12
= f.input :avatar, as: :shrine_file
Shrine features quite a few useful plugins out-of-the-box.
- Versions
The versions plugin allows you to create different versions of an uploaded image. E.g. a small, medium and large version. Read more about it here.
Here is an example of how to use it:
class ImageUploader < Shrine
include ImageProcessing::MiniMagick
plugin :versions, names: [:original, :large, :medium, :small]
def process(io, context)
return super(io, context) if context[:phase] != :store
original = io.download
size_1200 = resize_to_limit(original, 1200, 1200)
size_600 = resize_to_limit(size_1200, 600, 600)
size_300 = resize_to_limit(size_600, 300, 300)
{ original: original, large: size_1200, medium: size_600, small: size_300 }
end
end
- Validations
This plugin allows you to check dimensions and file type of uploaded images. Check it out here.
- Metadata
The Metadata plugin is used to store information about uploaded images. You can read more about it here.
- Background jobs
Shrine supports background image processing and deletion. You can read here how to set it up.
Here are a few articles which explain how to migrate from other uploaders to shrine: