New webinar: "The Remote Job Search: My Microverse Journey" with graduate Paul Rail
Watch Now

We have launched an English school for software developers. Practice speaking and lose your fear.


File uploads are an important aspect of many Rails applications. In this article, we will look at how we can perform direct uploads to a Cloudinary storage account. If you’ve been using Heroku, by now you should know that its filesystem is ephemeral. That means that any changes to the filesystem whilst the dyno is running only last until that dyno is shut down or restarted. It’s why we lose most of the image references after a while. But, thanks to Cloudinary - and other cloud-based image storage services like AWS - there is a solution.

Cloudinary is a cloud-based storage service that offers an end-to-end image and video management solution for web and mobile applications. There’s a lot of information on the internet about the advantages of using Cloudinary cloud storage service, as well as Amazon S3. I will leave you to do the searching while we focus on the purpose of this article.

If you haven’t read my previous article  -  Rails Image Upload in 7 Simple Steps Using Shrine - please review it first, as we will be using most of the steps outline there.

I've also created a step-by-step video tutorial to walk you through this concept and the below steps.

Application Setup

Let’s quickly create a new rails app and add the required gem using rails new direct_uploads_with_shrine -T. You can choose any app name of your choice - yes I know the name is long.

Add the following gem to your Gemfile:

{% code-block language="js" %}
gem 'image_processing', '~> 1.2'
gem 'shrine', '~> 3.3'
gem "shrine-cloudinary", "~> 1.1"
{% code-block-end %}

Under the group, development, and test, add the dotenv-rails gem. This gem allows us to hide our API-keys from prying eyes 😃.

{% code-block language="js" %}
group :development, :test do
   gem 'dotenv-rails'
### Run bundle install
{% code-block-end %}

Before we proceed, if you haven’t set up an account yet, it’s easy to on cloudinary.com. For this application we are building, you’ll need your cloud name, API key, and API secret.

Now, create .env file in the root directory and add your keys.

{% code-block language="js" %}
{% code-block-end %}

Notice, they are not in quotes. Don’t add any - just like that, save it and close. Add the .env file to .gitignore so it doesn’t get pushed to production or a public domain like GitHub to avoid exposing your keys.

Our aim is to typically upload photos directly to Cloudinary, so our shrine.rb under config/initializers would appear just a bit different from what we had when uploading to the file system.

{% code-block language="js" %}
require "cloudinary"
require "shrine/storage/cloudinary"
cloud_name: ENV['CLOUD_NAME'],
api_key:    ENV['CLOUD_API_KEY'],
api_secret: ENV['CLOUD_API_SECRET'],
Shrine.storages = {
cache: Shrine::Storage::Cloudinary.new(prefix: "cache"), # for direct uploads
store: Shrine::Storage::Cloudinary.new(prefix: "rails_uploads"),
Shrine.plugin :activerecord           # loads Active Record integration
Shrine.plugin :cached_attachment_data # enables retaining cached file across form redisplays
Shrine.plugin :restore_cached_data    # extracts metadata for assigned cached files
Shrine.plugin :validation_helpers
Shrine.plugin :validation
{% code-block-end %}

That’s it. 

We could just use store: Shrine::Storage::Cloudinary.new, but adding the prefix: 'rails_uploads' allows Shrine to create a directory rails_uploads in our Cloudinary account. This is a good way to keep things organized considering that we may use that same account for different apps.

Let's stop here for a moment. I ask that you continue from step 3 of  Rails Image Upload in 7 Simple Steps Using Shrine, so that I don’t need to bother you with the details, rather just the implementations.

Next Steps

Now generate your article scaffold and migrate.

{% code-block language="js" %}
rails generate scaffold Article title body:text image_data:text
rails db:create && rails db:migrate
{% code-block-end %}

Create your Image Uploader Class with your image validations.

{% code-block language="js" %}
# app/uploaders/image_uploader.rb
class ImageUploader < Shrine
 Attacher.validate do
   validate_mime_type %w[image/jpeg image/png image/webp]
   validate_max_size  1*1024*1024
{% code-block-end %}

Associate your model with the Shrine image attribute.

{% code-block language="js" %}
# app/models/article.rb
class Article < ApplicationRecord
 include ImageUploader::Attachment(:image)
validates :title, presence: true
{% code-block-end %}

Update your article params inside the article controller and views to reflect image instead of image_data

{% code-block language="js" %}
# app/controllers/articles_controller.rb
def article_params
 params.require(:article).permit(:title, :body, :image)
# app/views/articles/_form.html.erb
## Replace
<div class="field">
 <%= form.label :image_data %>
 <%= form.text_area :image_data %>
## With
<div class="field">
 <%= form.label :image %>
 <%= form.file_field :image %>
{% code-block-end %}

Modify your article show page to display the uploaded image.

{% code-block language="js" %}
# app/views/articles/show.html.erb
<strong>Image data:</strong>
<%= image_tag @article.image_url if @article.image %>
{% code-block-end %}

Don’t worry, we are done here.

Now, run your application, make sure there are no errors, and try out the upload. Open your Cloudinary account to confirm your uploaded photo. 

Upload images directly to your Cloudinary account

Great job!

By now, you should be comfortable using Shrine to upload images directly to your Cloudinary account.

Happy coding!

To learn more about Microverse, and joining our supportive community of remote software developers, get started below!

We have launched an English school for software developers. Practice speaking and lose your fear.

Subscribe to our Newsletter

Get Our Insights in Your Inbox

Career advice, the latest coding trends and languages, and insights on how to land a remote job in tech, straight to your inbox.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
We use own and third party cookies to; provide essential functionality, analyze website usages, personalize content, improve website security, support third-party integrations and/or for marketing and advertising purposes.

By using our website, you consent to the use of these cookies as described above. You can get more information, or learn how to change the settings, in our Cookies Policy. However, please note that disabling certain cookies may impact the functionality and user experience of our website.

You can accept all cookies by clicking the "Accept" button or configure them or refuse their use by clicking HERE.