Active Storage, now bundled with Ruby on Rails 5.2, allows you to easily handle your file upload, either to cloud platforms, such as Amazon S3 or Google Cloud Storage, or even upload right into your application directory, linking your uploads to your Active Record models with ease.
In order to start using Active Storage, you should have an existing Ruby on Rails 5.2 or greater application (freshly installed or upgraded), open your terminal in your application directory and type the following command:
rails active_storage:install
This will generate a brand new migration for the two necessary database tables. After this, all you need to do is run the migrations with:
rails db:migrate
After this everything is ready to start using Active Storage.
Setting up
To start you should declare in your config/storage.yml
file your Active Storage services, one for each service that you use in your application.
Please, note that for the sake of this article, I’ll just be showing configurations for uploading the files to the same directory as your application, in your local disk. If you prefer to use it to upload to any cloud provider please refer to the docs.
local:
service: Disk
root: <%= Rails.root.join("storage") %>
After you declared every service that you’ll be using it’s time to tell your application what service should be used on each environment. In this article, I’m going to configure my development environment to use my local storage service. In order to achieve this add the following line to config/environments/development.rb
:
# Store files locally.
config.active_storage.service = :local
Use it
Now that we managed to configure Active Storage it’s time to start using it! One of the advantages of it is to easily create a relationship between the uploaded asset and your Active Record models, as stated at the beginning of this article, so we’re to take advantage of it. I have a Speakers model in which I intend to add an avatar to each of the speakers.
We’ll start by informing my Speakers model that it will have a one-to-one
relationship with the corresponding uploaded avatar. My model will look like:
class Speaker < ApplicationRecord
has_one_attached :avatar
end
Then my Speakers controller will accept an avatar to be uploaded. My speaker_params
will now look like:
def speaker_params
params.permit(:name, :company, :email, :avatar)
end
The file should now look like this:
class SpeakersController < APIController
before_action :set_speaker, only: %i[show update destroy] def index
@speakers = Speaker.all
render json: @speakers, status: :ok
end def create
@speaker = Speaker.create!(speaker_params)
render json: @speaker, status: :created
end def show
render json: @speaker, status: :ok
end def update
@speaker.update(speaker_params)
head :no_content
end def destroy
@speaker.destroy
head :no_content
end private def speaker_params
params.permit(:name, :company, :email, **:avatar**, :social_media)
end def set_speaker
@speaker = Speaker.find(params[:id])
end
end
I have to specify on my request body a field named avatar and its value will be a file, that’s going to be used as the speaker avatar.
After you finish your POST request and you navigate to the storage
directory you’ll see a newly created directory containing your uploaded file. If you wish to verify that the file is linked to your speaker you can start rails console
and type:
irb(main):001:0> speaker = Speaker.find(1)
=> #<Speaker id: 1, name: “Rafael”, email: “rafael.almeida@xing.com”, company: “XING”, social_media: nil, created_at: “2018–10–02 09:58:54”, updated_at: “2018–10–02 09:58:54”>
irb(main):002:0> app.url_for(speaker.avatar)
=> “http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--372a32ffdde03c2f2a37f407e63d10c909fd52ba/xing-photo.jpg"
Et voilá, Active Storage is configured and ready to be used.
Bonus tip
If you execute a GET request on your API you’ll get the information about your speaker that you defined on your migration, like the example below:
{
"id": 2,
"name": "Rafael",
"email": "rafael.almeida@xing.com",
"company": "XING",
"social_media": null,
"created_at": "2018-10-02T09:59:28.494Z",
"updated_at": "2018-10-02T09:59:28.497Z"
}
However, you also might want to get the associated avatar. We can use a serializer in order to achieve what we intend. You’ll need to add gem 'active_model_serializers'
and then include the following line in every controller that you want to get override with the serializer: include ActionController::Serialization
. After that you can use rails g serializer speaker
and Rails will generate the file for you.
In order to get the associated avatar your serializer
should look like:
class SpeakerSerializer < ActiveModel::Serializer
attributes :id, :name, :email, :company, :avatar
def avatar
rails_blob_path(object.avatar, only_path: true) if object.avatar.attached?
end
end
Now if you re-run your GET request you’ll see something like:
{
"speaker": {
"id": 2,
"name": "Rafael",
"email": "rafael.almeida@xing.com",
"company": "XING",
"avatar": "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19–515a0de8817b3529b5d3d168871cebf6ccee0463/xing-photo.jpg"
}
}
You’re now able to see the avatar associated with every speaker.
Top comments (0)