Use carrierwave to upload files to s3

JiaHung Lin
4 min readSep 3, 2017

--

Sometimes we need to upload files to server, we can use the “carrierwave” gem to help us. We choose the s3 as our server and we will deploy the app to heroku.

If you want to try it yourself, you can just reference the following 2 resources and skip the rest of the article.

  • You can check the doc of carrierwave first for detail information.
  • For uploading to s3, you can check this video from railscast.

Set AWS S3 account

You can check this video tutorial for detail process.

gem

Install “imagemagick”

$ brew install imagemagick

The reason we can’t install it by gem install command is because imagemagick is written by C, not Ruby.

If you want to use imagemagick, you had better install it before installing carrierwave and mini_magick to prevent some errors.

gem 'carrierwave'
gem 'mini_magick'
gem 'fog-aws'

The fog-aws gem will help us to upload file to s3.

Goals

This article will show you how to create a new video, which includes the small cover image and large cover image. We will upload the files to s3 via carrierwave gem.

Set tables

Create the essential columns for video

def change
create_table :videos do |t|
t.string :title
t.text :description
t.integer :category_id
t.string :small_cover # small cover of video
t.string :large_cover # large cover of video
t.string :video_url
t.timestamps
end
end

Create uploader

$ rails generate uploader large_cover
$ rails generate uploader small_cover

Edit the uploader files

app/uploaders/large_cover_uploader.rb

class LargeCoverUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process :resize_to_fill => [665, 375] # the defualt image size# 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
end

app/uploaders/small_cover_uploader.rb

# app/uploaders/small_cover_uploader.rb
class SmallCoverUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process :resize_to_fill => [166, 236] # the defualt image size# 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
end

Mount the uploader

Mount the uploader in video.rb

app/models/video.rb:

class Video < ActiveRecord::Base
mount_uploader :small_cover, SmallCoverUploader
mount_uploader :large_cover, LargeCoverUploader
end

Upload to s3

You can reference the code in Using Amazon S3 section.

Then we need to edit the carrierwave.rb.

app/initializers/carrierwave.rb:

CarrierWave.configure do |config|
if Rails.env.staging? || Rails.env.production?
config.fog_provider = ‘fog/aws’
config.fog_credentials = {
:provider => ‘AWS’,
:aws_access_key_id => ENV[‘AWS_ACCESS_KEY_ID’],
:aws_secret_access_key => ENV[‘AWS_SECRET_ACCESS_KEY’],
:region => ‘ap-northeast-1’ # Tokyo
}
config.fog_directory = ENV[‘S3_BUCKET_NAME’]
config.storage = :fog
else
config.storage = :file
config.enable_processing = Rails.env.development?
end
end
```

Since we want to deploy our code to heroku, you need some ways to set your api key. While setting some sensitive data like api key, we need some safer way to store it, instead of hardcore the data then make it public. The better way to store the informaiton is thorugh environment variable.

You can check this page for more detail.

In this post we will set the environment variable directly on heroku.

In your console, type

$ heroku config:set AWS_ACCESS_KEY_ID=sdfafgjioas
$ heroku config:set AWS_SECRET_ACCESS_KEY_ID=sdfafgjioasjdogijaas
$ heroku config:set S3_BUCKET_NAME=your_bucket_name

Or you You can also edit config vars on your app’s settings tab on Dashboard.

Go to dashboard => your app => setting => click “Reveal Config Vars”

Then you can set the Config vars.

Check this article for more information.

Set route

resources :videos, only: [:new, :create]

Create view

The view will look like.

Basically we won’t upload video directly to s3, since it will take too much space and cost a lots. You can use service like Youtube, Vimeo or Wista.

The view template

app/controller/admin/video/new.html.haml

%section.admin_add_video
.container
.row
.col-md-10.col-md-offset-1
= bootstrap_form_for [:admin, @video], layout: :horizontal, label_col: “col-sm-3”, control_col: “col-sm-6”, html: {class: “form-horizontal”} do |f|
%ul.nav.nav-tabs
%li
%a(href=”/ui/admin_views_payments”) Recent Payments
%li.active
%a(href=””) Add a New Video
%br
%fieldset
= f.text_field :title, control_col: “col-sm-3”
= f.select :category_id, options_for_select(Category.all.map {|category| [category.name, category.id]})
= f.text_area :description, rows: 8
= f.file_field :large_cover, class: “btn btn-file”
= f.file_field :small_cover, class: “btn btn-file”
= f.text_field :video_url, label: “Video URL”
%fieldset.actions.form-group
.col-sm-6.col-md-offset-3
%input(type=”submit” value=”Add Video” class=”btn btn-default”)

Set controller

class Admin::VideosController < ApplicaitonController
def new
@video = Video.new
end

def create
@video = Video.new(video_params)

if video.save
flash[:success] = “You have created the #{video.title}”
redirect_to new_video_path
else
flash[:error] = “Please check the error, something wrong with your input.”
render :new
end
end

private

def video_params
params.require(:videos).permit!
end
end

Note: If you have the following error when uploading to s3"uninitialized constant CarrierWave::Storage::Fog”, there are some tips to solve it.

1.Create a fog.rb file under lib directory

touch lib/fog.rb

2.Add following content in the file

module Fog
# no content
end

3.Edit config/initializers/carrier_wave.rb:

require ‘carrierwave/storage/abstract’
require ‘carrierwave/storage/file’
require ‘carrierwave/storage/fog’
CarrierWave.configure do |config|
# code emitted
end
```

Check evadne’s answer in this article.

--

--