CarrierWave
In this article we will discuss the process of how to upload an image using carrierwave.
Table Of Contents
What is CarrierWave?
CarrierWave is a gem that provides a simple and extremely flexible way to upload files from Ruby applications. It works well with Rack based web applications, such as Ruby on Rails.
How to install carrierwave
Reminder:
Make sure that your containers are up and running.
In your Gemfile, add gem carrierwave
.
gem 'carrierwave'
Then run bundle install
.
root@0122:/usr/src/app# bundle install
Fetching gem metadata from https://rubygems.org/..........
Fetching carrierwave 2.2.3
Installing carrierwave 2.2.3
Bundle complete! 17 Gemfile dependencies, 79 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Usage
To use the carrierwave image upload, you need to generate the uploader file.
root@0122:/usr/src/app# rails generate uploader Image
create app/uploaders/image_uploader.rb
Read Carefully:
You can set an allowlist of acceptable extensions or content types in CarrierWave. If you are mounting the uploader, uploading a file with the incorrect extension will render the record invalid. Otherwise, an error message is displayed.
Uncomment the extension_allowlist
method on your ImageUploader
. The only allowed file extensions are jpg
, jpeg
, gif
, and png
.
# app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# ...
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# ...
# For images you might use something like this:
- # def extension_allowlist
- # %w(jpg jpeg gif png)
- # end
+ def extension_allowlist
+ %w(jpg jpeg gif png)
+ end
# ...
end
Read Carefully:
You may use different kinds of storage in carrierwave, such as “filesystem” or “cloud storage”, all you need to do is specify the storage in your uploader, in our case, it’s
storage :file
for filesystem. You may also indicate where the uploaded files should be saved with the use of methodstore_dir
. See theImageUploader
above for reference.
To explain what happens here, the uploaded file will be stored in the filesystem, meanwhile, only the file directory (string
) will be saved on your database (column where you mounted the uploader).
To do that, let’s add image
column with type string to our posts table.
root@0122:/usr/src/app# rails g migration AddImageToPosts image:string
invoke active_record
create db/migrate/xxxxxxxxxxxxxx_add_image_to_posts.rb
root@0122:/usr/src/app# rails db:migrate
== xxxxxxxxxxxxxx AddImageToPosts: migrating ==================================
-- add_column(:posts, :image, :string)
-> 0.0076s
== xxxxxxxxxxxxxx AddImageToPosts: migrated (0.0078s) =========================
Mount the ImageUploader
to your image column in post model.
# app/models/post.rb
class Post < ApplicationRecord
# ...
belongs_to :user
+ mount_uploader :image, ImageUploader
# ...
end
On your posts form, add file field for image.
<!-- app/views/posts/_form.html.erb -->
<%= form_with model: post do |form| %>
<!-- ... -->
+ <div>
+ <%= form.file_field :image %>
+ </div>
<%= form.submit %>
<% end %>
Permit image parameter on PostsController
.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
# ...
private
# ...
def post_params
- params.require(:post).permit(:title, :content, :image)
+ params.require(:post).permit(:title, :content, :image, category_ids: [])
end
# ...
end
Finally, display the image to your posts index page.
<!-- app/views/posts/index.html.erb -->
<!-- ... -->
<td>comments count</td>
+ <td>image</td>
<td>action</td>
</thead>
<% @posts.each do |post| %>
<!-- ... -->
<td><%= post.comments_count %></td>
+ <td><%= image_tag post.image.url if post.image.present? %></td>
<!-- ... -->
Reminder:
Before the
image_tag
method, we must include a condition (if post.image.present?
) that checks whether the image exists. Otherwise, an error will occur if the post does not have an image since you are giving a nil url onimage_tag
method.
An illustration of such error is shown below.
Now we already integrated the image upload in our application.
Before you commit the changes that you’ve made during the course of this tutorial, you should take note that the uploaded images should not be committed and push to your remote repository since we don’t need it in our codebase. But if we take a look at the current uncommitted changes:
$~/KodaCamp> git status -u ✔ 11:41:14
On branch master
Your branch is up to date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
public/uploads/post/image/xx/xxxx.png
nothing added to commit but untracked files present (use "git add" to track)
You will see that the uploaded images can be seen in the untracked files. Meaning you and the other developers that have access to your project can commit and push the uploaded images to your remote repository even though these files are not needed. This will cause confusion and inconvenience to you and to those developers. So we have to make a restriction that prevents everyone from committing uploaded files. To do this, we should add the directory to which the uploaded images are stored in our .gitignore
file.
# .gitignore
# ...
/public/assets
+ /public/uploads
# ...
A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected. So if you want to ignore a file that you’ve committed in the past, you’ll need to delete the file from your repository and then add a .gitignore
rule for it. In our case any files stored in /public/uploads
will be ignored by git.
Let’s check our untracked changes again.
$~/KodaCamp> git status -u ✔ 11m 34s 13:31:35
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .gitignore
no changes added to commit (use "git add" and/or "git commit -a")
The uploaded images are now ignored by Git.