Advertisement
  1. Code
  2. Ruby on Rails

Meng-upload dengan Rails dan Carrierwave

Scroll to top
Read Time: 11 min

() translation by (you can also view the original English article)

Ini adalah artikel lain dalam seri "Mengunggah dengan Rails". Hari ini kita akan bertemu Carrierwave salah satu solusi untuk file uploading paling populer untuk Rails. Saya menyukai Carrierwave karena mudah untuk digunakan, memiliki banyak fitur yang diluar dugaan, dan menyediakan lusinan artikel "how to" yang ditulis oleh anggota komunitas, jadi anda tidak akan bingung dalam menggunakan Carrierwave.

Dalam artikel ini, Anda akan belajar cara:

  • Mengintegrasikan Carrierwave ke aplikasi Rails anda
  • Menambahkan validasi
  • Mempertahankan file dari seluruh request
  • Menghapus file
  • Membuat thumbnail
  • Mengunggah file dari lokasi jarak jauh
  • Memperkenalkan cara uploading multiple file
  • Menambahkan dukungan untuk penyimpanan cloud

Source code dalam artikel ini tersedia di GitHub. Selamat membaca!

Peletakan Pondasi

Seperti biasanya, mari mulai dengan membuat aplikasi Rails yang baru:

1
rails new UploadingWithCarrierwave -T

Untuk demo kali ini saya akan menggunakan Rails 5.0.2. Harap dicatat bahwa Carrierwave 1 hanya mendukung Rails 4+ dan Ruby 2. Jika Anda masih menggunakan Rails 3, maka hubungkan dengan Carrierwave versi 0.11.

Untuk melihat bagaimana Carrierwave bekerja, kita akan membuat aplikasi blogging yang sangat sederhana dengan model Post tunggal. Aplikasi ini akan memiliki atribut utama sebagai berikut:

  • title (string)
  •  body (text)
  • image (string) — bagian ini akan berisi gambar (nama file gambar, tepatnya) yang akan dilampirkan pada post.

Generate dan jalankan perintah migrate:

1
rails g model Post title:string body:text image:string
2
rails db:migrate

Mengatur beberapa routes:

config/routes.rb

1
resources :posts
2
root to: 'posts#index'

Lalu, buatlah sebuah basic controller.

posts_controller.rb

1
class PostsController < ApplicationController
2
  before_action :set_post, only: [:show, :edit, :update]
3
4
  def index
5
    @posts = Post.order('created_at DESC')
6
  end
7
8
  def show
9
  end
10
11
  def new
12
    @post = Post.new
13
  end
14
15
  def create
16
    @post = Post.new(post_params)
17
    if @post.save
18
      redirect_to posts_path
19
    else
20
      render :new
21
    end
22
  end
23
24
  def edit
25
  end
26
27
  def update
28
    if @post.update_attributes(post_params)
29
      redirect_to post_path(@post)
30
    else
31
      render :edit
32
    end
33
  end
34
35
  private
36
37
  def post_params
38
    params.require(:post).permit(:title, :body, :image)
39
  end
40
41
  def set_post
42
    @post = Post.find(params[:id])
43
  end
44
end

Sekarang, mari membuat index index view:

views/posts/index.html.erb

1
<h1>Posts</h1>
2
3
<%= link_to 'Add post', new_post_path %>
4
5
<%= render @posts %>

Parsial yang sesuai:

views/posts/_post.html.erb

1
<h2><%= link_to post.title, post_path(post) %></h2>
2
3
<p><%= truncate(post.body, length: 150) %></p>
4
5
<p><%= link_to 'Edit', edit_post_path(post) %></p>
6
<hr>

Di sini saya menggunakan metode Rails truncate hanya untuk menampilkan 150 simbol pertama dari postingan. Sebelum kita membuat tampilan lain dan sebuah bentuk parsial, mari kita mengintegrasikan Carrierwave ke dalam aplikasi.

Integrasi Carrierwave

Masukkan gem baru ke dalam Gemfile:

Gemfile

1
gem 'carrierwave', '~> 1.0'

Jalankan:

1
bundle install

Carrierwave menyimpan konfigurasinya di dalam uploaders yang terdapat dalam model Anda. Untuk membuat uploader, gunakan perintah berikut:

1
rails generate uploader Image

Sekarang, di dalam app/uploaders, Anda akan menemukan file baru bernama image_uploader.rb. Perhatikan bahwa ini memiliki beberapa perintah dan contoh yang berguna, jadi Anda dapat menggunakannya untuk memulai app. Dalam demo ini kita akan menggunakan ActiveRecord, tetapi juga mendukung penggunaan Mongoid, Sequel, dan DataMapper.

Selanjutnya, kita perlu menyertakan atau mount uploader ini ke dalam model:

models/post.rb

1
mount_uploader :image, ImageUploader

Uploader sudah memiliki pengaturan default, tetapi setidaknya kita harus memilih di mana file yang diunggah akan disimpan. Untuk saat ini, mari gunakan penyimpanan file:

uploaders/image_uploader.rb

1
storage :file

Secara default, file akan ditempatkan di dalam direktoripublic/uploads, ini adalah cara terbaik untuk mengeluarkannya dari version system control:

.gitignore

1
public/uploads

Anda juga dapat memodifikasi store_dir method di dalam uploader Anda agar dapat memilih beberapa lokasi lain.

Pada titik ini, kita dapat membuat tampilan baru dan sebuah formulir parsial untuk mulai mengunggah file:

views/posts/new.html.erb

1
<h1>Add post</h1>
2
3
<%= render 'form', post: @post %>

views/posts/_form.html.erb

1
<%= form_for post do |f| %>
2
  <div>
3
    <%= f.label :title %>
4
    <%= f.text_field :title %>
5
  </div>
6
7
  <div>
8
    <%= f.label :body %>
9
    <%= f.text_area :body %>
10
  </div>
11
12
  <div>
13
    <%= f.label :image %>
14
    <%= f.file_field :image %>
15
  </div>
16
17
  <%= f.submit %>
18
<% end %>

Perhatikan bahwa PostsController tidak perlu dimodifikasi karena kita sudah mengizinkan atribut image

Terakhir, mari membuat edit view:

views/posts/edit.html.erb

1
<h1>Edit post</h1>
2
3
<%= render 'form', post: @post %>

Nah! Anda dapat mem-boot server dan mencoba membuat sebuah postingan dengan gambar. Masalahnya adalah gambar ini tidak terlihat di mana pun, jadi mari kita lanjutkan ke bagian selanjutnya dan tambahkan show page!

Menampilkan Gambar

Sekarang, satu-satunya view yang belum kita buat adalahs show. Tambahkan source code ini sekarang:

views/posts/show.html.erb

1
<%= link_to 'All posts', posts_path %>
2
<h1><%= @post.title %></h1>
3
4
<%= image_tag(@post.image.url, alt: 'Image') if @post.image? %>
5
6
<p><%= @post.body %></p>
7
8
<p><%= link_to 'Edit', edit_post_path(@post) %></p>

Seperti yang Anda lihat, menampilkan attachment sangat mudah: semua yang perlu Anda lakukan adalah mengetikkan @post.image.urluntuk mengambil URL image. Untuk mendapatkan path ke file, gunakan metode current_path. Perhatikan bahwa Carrierwave juga menyediakan image? method bagi kita untuk memeriksa apakah attachment benar-benar ada ( image itu sendiri tidak akan pernah kembali dalam keadaan nil, bahkan jika file tersebut tidak ada).

Sekarang, setelah melakukan navigasi ke post, Anda akan melihat gambar, tetapi mungkin gambar tampak terlalu besar: karena kita tidak membatasi ukuran dimensinya. Tentu saja, kita dapat mengecilkan gambar dengan beberapa aturan CSS, tetapi jauh lebih baik untuk membuat thumbnail setelah file diunggah. Untuk membuatnya, bagaimanapun, membutuhkan beberapa langkah tambahan.

Membuat Thumbnail

Untuk memangkas dan memperbesar gambar, kita membutuhkan tool yang terpisah. Carrierwave memiliki dukungan yang mengejutkan untuk RMagick dan MiniMagick gem yang, pada gilirannya, digunakan untuk memanipulasi gambar dengan bantuan ImageMagick. ImageMagick adalah aplikasi open-source yang memungkinkan Anda untuk mengedit gambar yang ada dan menghasilkan yang baru, jadi sebelum melanjutkan Anda perlu download tool ini dan meng-install-nya. Selanjutnya, Anda bebas memilih salah satu dari dua gem itu. Saya selalu menggunakan MiniMagick, karena lebih mudah untuk menginstalnya dan memiliki dukungan yang lebih baik:

Gemfile

1
gem 'mini_magick'

Jalankan:

1
bundle install

Lalu, masukkan MiniMagick kedalam uploader:

uploaders/image_uploader.rb

1
include CarrierWave::MiniMagick

Sekarang kita hanya perlu memperkenalkan version baru ke uploader yang sudah kita miliki sebelumnya. Konsep versions (atau styles) digunakan di banyak file yang mengunggah library; maksudnya adalah file tambahan berdasarkan lampiran asli akan dibuat, misalnya, dengan dimensi atau format yang berbeda. Memperkenalkan version baru yang disebut thumb:

uploaders/image_uploader.rb

1
version :thumb do
2
    process resize_to_fill: [350, 350]
3
end

Anda mungkin memiliki banyak version yang anda suka dan version dapat dibuat di atas version yang lain:

uploaders/image_uploader.rb

1
version :small_thumb, from_version: :thumb do
2
    process resize_to_fill: [20, 20]
3
end

Jika anda sudah mengunggah beberapa gambar, mereka tidak akan memiliki thumbnail yang tersedia. Ini bukan masalah, karena anda dapat membuatnya kembali menggunakan konsol Rails:

1
rails c
2
Post.find_each {|post| post.image.recreate_versions!(:thumb) if post.image?}

Terakhir, tampilkan thumbnail anda dengan tautan ke gambar asli:

views/posts/show.html.erb

1
<%= link_to(image_tag(@post.image.thumb.url, alt: 'Image'), @post.image.url, target: '_blank') if @post.image? %>

Boot servernya dan amati hasilnya!

Menambahkan validasi

Saat ini pengunggahan kita sudah berfungsi, tetapi kita tidak memvalidasi apa saja yang user masukan sama sekali, yang tentu saja buruk. Jika kita ingin bekerja hanya dengan gambar, mari kita whitelist .png, .jpg dan .gif extensions:

uploaders/image_uploader.rb

1
def extension_whitelist
2
    %w(jpg jpeg gif png)
3
end

Anda juga dapat menambahkan pemeriksaan jenis konten dengan menuliskan metode content_type_whitelist:

uploaders/image_uploader.rb

1
def content_type_whitelist
2
    /image\//
3
end

Selain itu, sangat mungkin untuk melakukan blacklist terhadap beberapa jenis file, misalnya file yang dapat dieksekusi, dengan menggunakan metode content_type_blacklist.

Selain memvalidasi jenis dan ekstensi file, mari kita membuat file yang diunggah harus kurang dari 1 megabyte. Untuk melakukannya, kita akan membutuhkan tambahan gem yang mendukung validasi untuk ActiveModel:

Gemfile

1
gem 'file_validators'

Lalu, lakukan instalasi:

1
bundle install

Sekarang perkenalkan validasi yang diinginkan (perhatikan bahwa saya juga menambahkan pemeriksaan untuk atribut title dan body):

models/post.rb

1
validates :title, presence: true, length: {minimum: 2}
2
validates :body, presence: true
3
validates :image, file_size: { less_than: 1.megabytes }

Hal berikutnya yang harus dilakukan adalah menambahkan I18n translation untuk error messages dari Carrierwave:

config/locales/en.yml

1
en:
2
  errors:
3
    messages:
4
      carrierwave_processing_error: "Cannot resize image."
5
      carrierwave_integrity_error: "Not an image."
6
      carrierwave_download_error: "Couldn't download image."
7
      extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
8
      extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"

Saat ini, kita tidak menampilkan kesalahan validasi di mana saja, jadi mari buat shared partial:

views/shared/_errors.html.erb

1
<% if object.errors.any? %>
2
  <h3>Some errors were found:</h3>
3
  <ul>
4
    <% object.errors.full_messages.each do |message| %>
5
      <li><%= message %></li>
6
    <% end %>
7
  </ul>
8
<% end %>

Gunakan partial ini ke dalam form:

views/posts/_form.html.erb

1
<%= render 'shared/errors', object: post %>

Sekarang coba unggah beberapa file tidak valid dan amati hasilnya. Ini seharus berhasil, tetapi jika anda memilih file yang valid dan tidak mengisi judul atau badan, maka cek akan tetap gagal dan pesan error akan ditampilkan. Namun, file akan dihapus dan user harus memilih gambar lagi, hal ini sangat tidak nyaman. Untuk memperbaikinya, kita perlu menambahkan field lain ke form.

Mempertahankan File di Seluruh Request

Mempertahankan seluruh file di formulir redisplay is actually quite easy. All you need to do is add a new hidden field and permit it inside the controller:

views/shared/_form.html.erb

1
<%= f.label :image %>
2
<%= f.file_field :image %><br>
3
<%= f.hidden_field :image_cache %>

posts_controller.rb

1
params.require(:post).permit(:title, :body, :image, :image_cache)

Sekarang image_cache akan terisi secara otomatis dan gambar tidak akan hilang. Mungkin akan berguna untuk menampilkan thumbnail juga sehingga pengguna memahami gambar telah berhasil diproses sebelumnya:

views/shared/_form.html.erb

1
<% if post.image? %>
2
    <%= image_tag post.image.thumb.url %>
3
<% end %>

Menghapus Gambar

Fitur lain yang sangat umum adalah kemampuan untuk remove attached files saat mengedit records. Dengan Carrierwave, menerapkan fitur ini tidak menjadi masalah. Tambahkan checkbox baru ke form:

views/shared/_form.html.erb

1
<% if post.image? %>
2
    <%= image_tag post.image.thumb.url %>
3
    <div>
4
      <%= label_tag :remove_image do %>
5
        Remove image
6
        <%= f.check_box :remove_image %>
7
      <% end %>
8
    </div>
9
<% end %>

Dan izinkan atribut remove_image:

posts_controller.rb

1
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache)

Nah itu dia! Untuk menghapus sebuah gambar secara manual, gunakan remove_image! method:

1
@post.remove_image!

Meng-upload dari Lokasi Jarak Jauh

Carrierwave juga menyediakan fitur yang sangat keren: kemampuan untuk mengunggah file dari lokasi yang jauh hanya dengan URL mereka. Mari tambahkan kemampuan ini sekarang dengan menuliskan bidang baru dan mengizinkan atribut terkait:

views/shared/_form.html.erb

1
<%= f.text_field :remote_image_url %>
2
<small>Enter URL to an image</small>

posts_controller.rb

1
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url)

Betapa kerennya itu? Anda tidak perlu melakukan perubahan sama sekali, dan Anda dapat segera menguji fitur ini!

Melakukan Beberapa Upload Sekaligus

Misalkan kita ingin postingan kita memiliki beberapa lampiran yang tersedia. Dengan pengaturan saat ini tidak mungkin, tapi untungnya, Carrierwave supports skenario seperti itu juga. Untuk mengimplementasikan fitur ini, Anda perlu menambahkan serialized field (untuk SQLite) atau JSON (untuk Postgres atau MySQL). Saya lebih memilih opsi yang terakhir, jadi mari kita beralih ke adaptor database baru sekarang. Hapus sqlite3 gem dari Gemfile dan tambahkan pg sebagai gantinya:

Gemfile

1
gem 'pg'

Lakukan instalasi:

1
bundle install

Modifikasi konfigurasi database seperti dibawah ini:

config/database.yml

1
default: &default
2
  adapter: postgresql
3
  pool: 5
4
  timeout: 5000
5
6
development:
7
  <<: *default
8
  database: upload_carrier_dev
9
  username: 'YOUR_USER'
10
  password: 'YOUR_PASSWORD'
11
  host: localhost

Membuat database Postgres sesuai, lalu generate dan terapkan migration:

1
rails g migration add_attachments_to_posts attachments:json
2
rails db:migrate

Jika Anda lebih memilih untuk tetap menggunakan SQLite, ikuti petunjuk yang tercantum dalam dokumentasi Carrierwave.

Sekarang lakukakn mount uploader (perhatikan plural form!):

model/post.rb

1
mount_uploaders :attachments, ImageUploader

Saya menggunakan uploader yang sama untuk attachment, tetapi tentu saja anda dapat membuat yang baru dengan konfigurasi yang berbeda.

Tambahkan multiple file field ke form anda:

views/shared/_form.html.erb

1
<div>
2
    <%= f.label :attachments %>
3
    <%= f.file_field :attachments, multiple: true %>
4
</div>

Selama attachments field akan berisi sebuah array, itu harus diizinkan dengan cara berikut:

posts_controller.rb

1
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url, attachments: [])

Terakhir, Anda dapat iterate ulang attachment postingan anda dan menampilkannya seperti biasa:

views/shared/show.html.erb

1
<% if @post.attachments? %>
2
  <ul>
3
    <% @post.attachments.each do |attachment| %>
4
      <li><%= link_to(image_tag(attachment.thumb.url, alt: 'Image'), attachment.url, target: '_blank') %></li>
5
    <% end %>
6
  </ul>
7
<% end %>

Perhatikan bahwa setiap attachment akan memiliki thumbnail seperti yang kita konfigurasikan di ImageUploader. Bagus!

Menggunakan Cloud Storage

Menggunakan file storage tidak selalu mudah dan / atau mungkin, misalnya, pada Heroku tidak mungkin menyimpan file kustom. Oleh karena itu Anda mungkin bertanya bagaimana cara memasangkan Carrierwave dengan cloud storage Amazon S3? Nah, itu tugas yang cukup mudah. Carrierwave bergantung pada gem fog-aws untuk menerapkan fitur ini:

Gemfile

1
gem "fog-aws"

Lakukan instalasi:

1
bundle install

Mari kita buat penginisialisasi untuk Carrierwave dan konfigurasi cloud storage secara global:

config/initializers/carrierwave.rb

1
CarrierWave.configure do |config|
2
  config.fog_provider = 'fog/aws'
3
  config.fog_credentials = {
4
      provider:              'AWS',
5
      aws_access_key_id:     ENV['S3_KEY'],
6
      aws_secret_access_key: ENV['S3_SECRET'],
7
      region:                ENV['S3_REGION'],
8
  }
9
  config.fog_directory  = ENV['S3_BUCKET']
10
end

Ada beberapa opsi lain yang tersedia, yang dapat ditemukan di dokumentasi.

Saya menggunakan dotenv-rails gem untuk mengatur variabel lingkungan dengan cara yang aman, tetapi Anda dapat memilih opsi lain. Namun, pastikan bahwa S3 key pair Anda tidak tersedia untuk umum, karena jika tidak, siapa pun dapat mengunggah apa pun ke storage Anda!

Selanjutnya, ganti storage baris storage :file dengan:

uploaders/image_uploader.rb

1
storage :fog

Selain S3, Carrierwave mendukung uploads ke Google Storage dan Rackspace. Layanan ini juga mudah diatur.

Kesimpulan

Cukup untuk hari ini! Kami telah mencakup semua fitur utama Carrierwave, dan sekarang Anda dapat mulai menggunakannya dalam proyek Anda. Carrierwave memiliki beberapa opsi tambahan yang tersedia, jadi silahkan pelajari dokumentasi.

Jika Anda bingung, jangan ragu untuk memposting pertanyaan Anda. Juga, anda mungkin dapat mengakses Carrierwave's wiki, yang mengposting artikel "how to" yang berguna untuk menjawab banyak pertanyaan umum.

Jadi saya berterima kasih untuk tetap bersama saya, dan happy coding!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.