Uploading Images With Carrierwave to S3 on Rails

Asif Ahmed

At some point in your Rails development you will want a user (admin or normal user) to upload images. Often, you will want those images to be resized to specific dimensions (don’t resize images with CSS!) and you will most likely want several differently sized images. You could store these files on the file system, but a much better solution is to upload them to an online file storage service. Let’s walk through a way to upload images with the Carrierwave gem to S3.

We are going to walk through a sample app on uploading images. (If you want to check out the final product you can always check it out here on Github) I’m going to assume you have Rails installed on your development machine but if not you can check out this tutorial. Once you have Rails installed on your machine we can start. Let’s create the project.

Once you are in the project directory. Let’s edit the Gemfile and add the following lines to the bottom.

Carrierwave is the gem that allows image uploads, MiniMagick allows use to resize and crop images, and lastly Fog allows us to upload images to S3 with ease. (Side-note: Fog is really powerful and let’s you manage a lot of Amazon Web Services [AWS], but we will just be using it to upload images to S3).

Once you are done adding the previous gems to the Gemfile you can go ahead and install all the gems.

Let’s create a model where we will end up storing the URL for our image uploads. I chose to call my model “uploads” with only one field called “name”. You can choose to be far more creative! First via the command line run:

Here is a copy of my migration file:

Once you have your migration file all set up, feel free to migrate it.

We have the table set up, but we need our model. Here is a copy on my model file (app/models/upload.rb). The mount_uploader is how we will be linking the name field with a URL of where the file is stored.

We also have a class called ImageUploader…but we haven’t defined it yet. Let’s go ahead and do that (app/uploaders/image_uploader.rb). This one will be a rather large file.

We need to include MiniMagick (because we want different sized images). We also use Fog for storage (this uploads the images to S3). Alternatively you could use the file system for storage (storage :file). Using the file system is generally not recommended because this is not scalable (what if you have your Rails app running off of multiple machines?) and this is not performant (the Rails app has to serve up a large image file). You can change the name of the storage directory, but personally the defaults seemed fine. Finally I ended up creating 4 additional versions of the image (the original version is also stored in S3 in our scenario). I decided I wanted a large, medium, thumbnail, and a square version. You can read more about resizing in the Carrierwave documentation.

We have our migration, our model, our uploader. Now let’s change our routes files (config/routes.rb).

Replicated in full is the controller file which deals with the CRUD (creating, reading, updating and deleting ) of our “uploads” model (app/controllers/uploads_controller.rb).

Now all that’s left to do is create our view files and we are almost done. Below are the index (app/views/uploads/index.html.erb), new (app/views/uploads/new.html.erb), and show (app/views/uploads/show.html.erb) templates (respectively).

The view files require a little bit of explanation. In the show template, we have an upload model. However, how do we retrieve the url for the images we just saved. Remember we connected the name field to store the url so to retrieve it we could simply do something like “@upload.name.url”. However the gives us the URL for the original images we uploaded. What if we wanted a different version? Well, no problem – just use something like “@upload.name.version_type” and you will be good to go.

We are almost there! The only thing remaining is that we need to add our AWS credentials for S3 and set up S3. First log in to AWS and click on S3. Then create a bucket and note the name (you will need it). You also need to get your AWS credentials. To do so click on your name in the top tool bar around the right side. Then create some access keys. Amazon will prompt you to use “IAM” users, but you can ignore that for now (using IAM users is a best practice for security reason, but right now we just want our app up and running – we will come back to setting up IAM users in a later post). For more info about getting your access keys check our this article from Amazon. Once you have your access key, bucket name, and secret create a file in the config folder for S3 (config/initializers/s3.rb).

Now we have a functioning app where we can upload resized images to S3 with Carrierwave. Go forth and apply what you have learned in your actual Rails app. To check the source on Github click here.

Also, here is good SO answer regarding uploading files.

Remember to follow me on twitter. I regularly tweet coding solutions I find.

 

 

  • Anthony

    When I try to submit the image, the errors read this: Avatar Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: ImageMagick/GraphicsMagick is not installed. Any Idea what this means?

  • Muhammad Faisal Iqbal

    Its one the complete articles
    Thanks for writing it
    but I don’t understand usage of

    def set_upload
    @upload = Upload.find(params[:id])
    end
    can you explain it

  • Szilard Magyar

    Hey Asif, great article! I have a question if you don’t mind. I’d like to add extra versions besides my default, but I don’t know how I can get the new version on S3. So basically my problem is if I change my code from original_thumb to new_thumb then the app can’t find the new_thumb on S3 since it is not there and this could cause problem with users who registered before the code changed. Could you tell me what I should do?

    • Szilard Magyar

      In the meantime I solved this issue but ran into another one. You don’t seem to be having problems with image display ratio. At the moment I only use resize_to_fit for one version (1:1 ratio) and just realized that some profile images uploaded by users got distorted (I guess the image ratio of the images they uploaded were not 1:1). How did you solve this issue?

  • Christos Christoudias

    This is an outstanding article. It’s very comprehensive and well-written. Thanks so much.

  • Adam Bull

    This was great help thank you!