Member preview

Uploading images from Rails to DigitalOcean Spaces, with previewing

So I wanted to make my site a little bit more friendly looking, and using profile pictures seemed like a good idea. In my case, users can upload projects, which other people can join. There are no user accounts, every user is visiting as a guest, so the ‘profiles’ in this case is actually the projects. A user will be able to attach a profile picture to their project. It will look like this:

Here’s my requirements for this feature:
1: When creating a new project, there’s a field for uploading an image.
2: When user selects a file, it should pop up and be previewed inside the form.
3: When submitting the form, the picture gets saved and connected to a project.

I have a model Project, which I modify with the following line to belong_to a ProfilePic.

Now let’s create the ProfilePic model. First we’ll generate and run the migration.

rails g migration create_profile_pics image:string
rails db:migrate

We added the column image , which will be where Carrierwave saves the name of the file. Carrierwave will know that when saving a new ProfilePic object, the attribute image should be handled in a way so that the actual file gets uploaded to DigitalOcean, to a path like this:

/uploads/profile_pics/543/name_of_file.jpg

Where 543 will be the id of the new ProfilePic record, and only the name of the file gets store in our database.

Now let’s create the ProfilePic model.

The magic line here is on row number 2. This is what tells carrierwave that the image attribute is a magic one that it will need to take care of automatically for us. Now let’s add Carrierwave to our Gemfile and bundle install.

gem ‘carrierwave’
$ bundle install

Next, we need an uploader. This is the piece of code that uploads and processes our files. There are built in commands to use for generating these files with carrierwave. Just run rails generate uploader Image . A new file will be created for us at:

/app/uploaders/image_uploader.rb

We also need an initializer for carrierwave, we’ll add this in

/config/initializers/carrierwave.rb:

As you can can se at line #2 there, we are using fog/aws. That is because the fog-aws gem works perfectly fine with DigitalOcean spaces as well! We need to add that gem and bundle install.

gem ‘fog-aws’
$ bundle install

Now we’ll add the file field to our form.

I will skip all the CSS for the sake of keeping this article short and focused on the backend side of things. Basically, what I do client side is listening on the input change, and then replace my palceholder.png with the path to the image that was selected. It looks like this:

In our projects controller, we need to allow the attribute image to be passed, and then use this to create a new ProfilePic record and connect it to our Project.

Yay, we now have the full logic in place for uploading the images and attaching them to our Project. If we take a look at our Spaces folder at DigitalOcean it will look like this.

That’s our image. But! Since I want to resize the image so that we don’t have to serve it in original size to the client on each load and then resize it with CSS, we can add that logic in our uploader. To do this, we’ll use the mini_magick gem together will carrierwave.

gem ‘mini_magick’
$ bundle install

And then update our uploader to look like this:

We are now creating two versions of the image, one ‘thumb’, and one ‘preview’. To render the thumb version in a view, we can do this:

= image_tag(project.profile_pic.image_url(:thumb))

And that’s it! We now have nice little images to show on our page. Hope it helped. If you liked this article, please give me one of those clapping hands, and if you hated this article, let me know in the comments so I can get better! :)

— Eric

Twitter: @ercintheloft