Upload images with your Rails app using Active Storage and Cloudinary
December 8, 2020
☕️ 4 min read
Archived post. Originally posted on my blog in Medium
One of the first issues I encountered when deploying to production a Rails app that used Active Storage, is that I could no longer use its default local storage configuration as I did in the development environment. For example, if you are using a host like Heroku to deploy your app with Active Storage’s default setup, the images you upload will be stored only temporarily in Heroku’s filesystem.
Luckily, Active Storage supports uploading files to a cloud storage service like Amazon S3, Google Cloud Storage, Microsoft Azure Storage or, in my case, Cloudinary. Services like Amazon S3 are widely used, but they have the inconvenience that, even with access to a free quota, you have to provide credit card details and are exposed to unexpected charges if you exceed the quota accidentally. Considering I was setting this up for some hobby projects, I didn’t like the idea of facing a huge bill for accidentally overusing my cloud storage, like that story of a guy who got a bill of $2700.
That’s when I discovered Cloudinary. Cloudinary is a cloud storage service that offers a free plan, requiring no credit card, and allows storage of up to 25GB of data or 25GB of viewing bandwidth. Moreover, it has a Gem that simplifies integration with any Rails app. This covered all my needs, so I went ahead.
Install Cloudinary’s gem
To set up Cloudinary in your Rails app, first create a free account on their website. Then, as with any other Rails gem, add it to your Gemfile:
gem 'cloudinary'
and run:
bundle install
Set up your Cloudinary credentials
The next step is to add your Cloudinary credentials. You can find these in the cloudinary.yml
file, which you can download from your Cloudinary dashboard. As with any other credentials, it is good practice to store them in a configuration file outside of Git while adding them to your environment variables.
In order to do this, if you are running the app locally, you can use a Gem called Figaro. Once installed, Figaro creates a config/application.yml
file and adds it to your .gitignore. Therefore, in your config/application.yml
, add your credentials as in the example below:
development:
cloud_name: mycloud
api_key: '123456789'
api_secret: qwerty123uiop456
enhance_image_tag: 'true'
static_file_support: 'false'
production:
cloud_name: mycloud
api_key: '123456789'
api_secret: qwerty123uiop456
enhance_image_tag: 'true'
static_file_support: 'true'
test:
cloud_name: mycloud
api_key: '123456789'
api_secret: qwerty123uiop456
enhance_image_tag: 'true'
static_file_support: 'false'
Then, Figaro will parse this YAML file and load all the values into ENV.
However, when deploying the app to production, you will have to do this in a different way depending on the service you are using. For example, in Heroku you will have to add the credentials to your config vars. You can easily do this from your Heroku dashboard, just remember to write the variable names in upper case (API_KEY, API_SECRET, CLOUD_NAME, etc).
Configure Active Storage
Next is to set up Active Storage to work with Cloudinary. First you will have to declare the service in the config/storage.yml file:
cloudinary:
service: Cloudinary
Then configure Active Storage in the config/environments/production.rb
file commenting out the default local setup:
config.active_storage.service = :cloudinary
#config.active_storage.service = :local
This will change your production environment setup. If you are going to use Cloudinary in your development and test environments as well, just do the same in the corresponding development.rb and test.rb files.
In order to upload images to a cloud storage service, Active Storage will have to upload directly from the client to the cloud. This functionality is called Direct Uploads and it has to be included in your application’s JavaScript bundle.
If you are using an old version of Rails (5 or below) you will have to do this using the asset pipeline by adding the following line in app/assets/javascripts/application.js :
//= require activestorage
However, this is not relevant to new Rails 6 applications where Webpack is used. Instead you will have to use the npm package including the following line in app/javascript/packs/application.js :
require("@rails/activestorage").start()
Uploading and displaying images
Now you are able to upload images using Rails forms like in the example below:
<%= form_for @book do |f| %>
<%= f.file_field :cover, class: 'form-control', direct_upload: true %>
<%= f.button :submit, class:"button is-dark" %>
<% end %>
When displaying images from the cloud, you can still use the image_tag method as usual:
<%= image_tag @book.cover, alt: "#{@book.title}" %>