Basic image upload
我們會使用 CarrierWave 處理圖片上傳,並把圖片和 Micropost model 關聯起來。所以要在 Gemfile 增加 carrierwave gem;此外,也加上了 mini_magick 和 fog gem,前者用來處理調整圖片尺寸,後者是處理在 production 環境中上傳圖片:
Gemfile
source 'https://rubygems.org'
gem 'rails', '4.2.2'
gem 'bcrypt', '3.1.7'
gem 'faker', '1.4.2'
gem 'carrierwave', '0.10.0'
gem 'mini_magick', '3.8.0'
gem 'fog', '1.36.0'
gem 'will_paginate', '3.0.7'
gem 'bootstrap-will_paginate', '0.0.10'
.
.
.
然後執行 bundle:
$ bundle install
CarrierWave 自帶了一個 Rails 產生器,用於建立圖片上傳程序(image uploader),我們要建立一個名為 picture 的上傳程序:
$ rails generate uploader Picture
CarrierWave 上傳的圖片應該對應於 Active Record model 中的一個屬性,這個屬性只要儲存圖片名稱的字串即可,增加這個屬性的 Micropost data model 如下:

為了增加這個屬性,我們就要建立一個遷移,然後在 development database 中執行遷移:
$ rails generate migration add_picture_to_microposts picture:string
$ bundle exec rake db:migrate
我們要使用 mount_uploader 方法告訴 CarrierWave 把圖片和 model 關聯起來。第一個參數是屬性的 symbol 形式,第二個參數是上傳程序的 class name:
mount_uploader :picture, PictureUploader
PictureUploader class 是在 picture_uploader,rb 文件裡,之後才會編輯到,目前使用預設內容即可。
接著把上傳程序加進 Micropost model 裡:
app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
在某些系統中,可能必須重啟 Rails server,這樣測試才能通過。(如果使用 Guard,也必須重啟)
然後要在 microposts 表單加上 file_field tag:
app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="picture">
<%= f.file_field :picture %>
</span>
<% end %>
注意 form_for 指定了下面這個參數:
html: { multipart: true }
為了支持檔案上傳功能,必須指定這個參數。
最後,我們必須把 picture 增加到可以透過 web 修改的屬性列表中,也就是要修改 micropost_params 方法:
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
.
.
.
private
def micropost_params
params.require(:micropost).permit(:content, :picture)
end
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if @micropost.nil?
end
end
圖片上傳之後,我們可以使用 image_tag 輔助方法來渲染圖片:
app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>
注意這邊使用了 picture? 布林值方法,如果沒有圖片就不顯示 image_tag。這個方法由 CarrierWave 自訂建立,名稱由儲存圖片檔案名的屬性而定。
自己手動上傳圖片後的畫面如下:

針對圖片上傳的測試留做練習題。