Uploading Files Using CarrierWave In Rails

Dakota Lillie
8 min readOct 17, 2017

--

I’ve been teaching myself Ruby on Rails by using it to build a blog for my buddy. In the process, I‘ve found myself needing to figure out how to manage file uploads, so that the articles he posts will be able to have pictures associated with them. I read through several tutorials, and figured I’d share here what I’ve learned, in the hope that someone else might also find it useful.

A few logistical notes, before we get started: I’m running Rails 5.1.4 with CarrierWave 1.2.1 on Mac. Also, while I’ll do my best to explain as much as possible, there are some topics that are beyond the scope of this tutorial. To be able to follow along with this tutorial, you’ll need at least some cursory knowledge of Rails and the MVC paradigm.

We’re going to be building a basic blogging application, like the one I’ve been working on for my friend. I’ll be starting with basic setup, which you can skip if you’re implementing CarrierWave into a pre-existing app. The code for this tutorial is also available on Github.

Part 1: Basic Setup

Alrighty, let’s boot up our Rails app:

rails new MyProject -T

The -T flag tells Rails to generate the new application without the default test suite. We’re omitting it here because it is unnecessary for the purposes of this tutorial, but in practice, you would only do this if you were planning on using a different testing framework.

We’re then going to open our Gemfile and add some gems. Bootstrap is a styling library that’s going to help us get our site looking decent with minimal effort, and jQuery is a Javascript library that helps with animations and other DOM manipulations:

We’ll then install the gems:

bundle install

Next, we’re going to rename app/assets/stylesheets/application.css to app/assets/stylesheets/application.scss and add the following lines:

We’ll also add a few lines to the bottom of our app’s Javascript manifest, located at app/assets/javascripts/application.js:

Great! We’re going to need a couple other things before we get started—a controller and a model. Let’s generate those now (the g is short for “generate”):

rails g controller Posts
rails g model Post title:string body:text image:string
rails db:migrate

Our Post model will have a title, a body, and an associated image. Note that the image field is a string because it will be storing the reference to a given image’s filename, not the image file itself.

We can then then set up our routes file to point to our newly-created controller:

resources denotes that we’ll be using RESTful actions for our Posts controller, while root to: tells the browser which page to display as the “home page” for our site… in this case, the #index method within the Posts controller.

And set up our controller to point to our soon-to-be-created views:

Now we can get to work designing our layout. We’re going to be creating a bunch of views and partials — Let’s start by creating a partial at app/views/layouts/_navigation.html.erb that will hold the design for our navbar:

There’s a whole lot of Bootstrap going on here — if you’re unfamiliar, I’d recommend checking out Bootstrap’s excellent documentation.

In order to display this partial, we’re going to need to render it within our main application layout, located at app/views/layouts/application.html.erb:

We can then create our index view:

And our view for creating new posts:

And add a corresponding partial for displaying individual posts within the index view:

As well as another partial containing the form we’ll use to create and edit said posts:

Lastly, we’ll make a view for editing images, which will render the _form partial we just created:

And another view for displaying them (via show):

Phew! Notice how we’re using @post.image.url within the image_tag to point to the filename of the image for the post that we’re showing. Also the image? method used in @post.image? checks whether an attachment is present at all (since the image method itself will never return nil, even if no file is present).

Part 2: Getting Started With CarrierWave

Now, let’s begin working with CarrierWave. In the terminal we run:

rails g uploader Image

This generates a new file at app/uploaders/image_uploader.rb, which contains the configuration for CarrierWave. Note that this file contains a lot of comments and useful examples, so much of our interaction with it will consist simply of uncommenting out certain lines.

If you open it up, near the top you should see the line storage :file . This indicates that the uploaded files will stored locally, by default in in public/uploads directory. (You can also configure CarrierWave to store files in the cloud, which is useful when working with platforms like Heroku that don’t allow you to store files.)

We’ll now need to include, or mount, the uploader into the model we generated earlier. Open up models/post.rb and add the following line:

And that’s it! Nice and easy. Now if you boot up your rails server:

rails s

And navigate to localhost:3000 in your browser, you should be able to upload files and create new posts. Go ahead, give it a shot!

Part 3: Customization & Best Practices

While our simple setup is a suitable starting point, as it stands our application is not ready for public use. What if the user uploads a huge file, or uploads a file which isn’t an image? We need to validate and process the files they upload to make sure there’s no funny business.

Since we’ll only be working with images in certain file formats, let’s add the following lines to app/uploaders/image_uploader.rb (some of them should already be there, commented out):

The %w(…) is Ruby shorthand for creating an array with the items contained in the parentheses

We can additionally check a file’s size, but we’ll need to require an additional gem to do so. Let’s add that now:

And install it:

bundle install

Now we can check to make sure the uploaded image is less that a certain size (in this case, 2 megabytes):

That should be it! Now, if the user tries to upload an image that doesn’t meet our requirements, it won’t work — but it also won’t tell them why, which could be frustrating. To fix this, let’s give our app the ability to display error messages.

First, we’ll need to add the text we’ll want our error messages to display at config/locales/en.yml:

Then we’ll need to configure our views to display them. Let’s create a new partial at app/views/posts/_errors.html.erb:

And render this partial with our app/views/posts/_form.html.erb partial (I added it at the bottom):

Great! Now the user should get an error message beneath the form if they try to upload an image that violates our criteria.

This solves our first problem, but there’s another issue we should also try and tackle. Say the user uploads an image that is very large, but nevertheless within the scope of what we allow. On certain pages, we might want to display a smaller version of that image. We could do this with css rules, but loading such a large file to display at such a small size is inefficient, and will unnecessarily slow down the responsiveness of our webpage. A better solution would be to tell CarrierWave to generate a smaller, thumbnail version of any file the user uploads.

To do this, we’re going to need a gem called MiniMagick, which in turn relies on another tool called ImageMagick. So before we get started, we’re going to need to make sure ImageMagick is installed:

brew install imagemagick

We’ll then add MiniMagick to our Gemfile:

And include it into our uploader. There should be a line near the top that you can just uncomment:

Now we need to tell the uploader what to do. Fortunately there’s another line a little farther down that we can again just uncomment and tweak to our liking:

We can tell our views to display the thumbnail by using .image.thumb rather than just .image . For instance, we can tweak our _form partial so that now at the top it reads:

And that’s it! Now any files the user uploads will automatically have a thumbnail version generated and stored alongside the original image.

Part 4: Conclusion

That’s all I’m going to get into for now. There was a lot we didn’t cover, including remote uploads, multiple uploads, cloud storage, and further ways to process the uploads. If you want to learn more, I recommend looking through the documentation as well as this particular tutorial here, which is excellent and from which much of my own knowledge on the subject was derived. CarrierWave also has a Wiki, which has the answers to many frequently asked questions.

--

--