想架設一個聊天室,除了文字訊息之外,還會需要傳照片,除此之外,也會使用照片當作大頭貼,這樣上傳檔案的功能就必須要考慮到。因此今天要來介紹一個常用的套件,CarrierWave。這邊就用使用者的個人簡介( profile )頁面來加入大頭貼欄位為例。
為解說方便,我先快速建立基本的 CRUD 功能
$ rails generate scaffold profile nickname email avatar:string
簡單來說,scaffold 會長出 model、controller、view...等檔案,並且寫好基礎的 CRUD 功能。這邊我自己用 profile 命名,欄位有匿稱、信箱、大頭貼。
記得在 routes.rb 新增路徑!
套件安裝:
# Gemfile
gem 'carrierwave', '~> 2.1'
別忘了!
$ bundle install
uploader
先用 rails generate 出來
$ rails generate uploader Avatar
執行後,長出一個檔案avatar_uploader.rb
可以打開檔案看看裡面寫了些什麼~
在一開始設計時,就有先給欄位的話,這一步就不用做了!
若是後來要新增上傳大頭貼的功能,要記得開欄位儲存照片!
(本篇先來做單一檔案的上傳方式)
此篇建立的是 profile
資料表
$ rails g migration add_avatar_to_profile avatar:string
這只是建立 migration 檔案,記得要 rails db:migrate
小提醒:這邊存取照片為什麼資料型態是 string 呢?因為我們是存一段 url(網址),所以是個字串型態。
# app/models/profile.rb
class profile < ApplicationRecord
mount_uploader :avatar, AvatarUploader
end
mount 是掛載的意思,讓這個 model 可以有上傳文件功能。
先前在講 ActionCable,有提到強參數的問題,ruby 會避免使用者不小心或惡意寫入不該進資料庫的東西,所以需要經過清洗才能進資料庫的設計。
剛剛我們建立了資料表欄位,也要記得回 controller 修改清洗條件
# app/controllers/profile_controller.rb
def profile_params
params.require(:profile).permit(:nickname, :email, :avatar)
end
用 scaffold 產生的表單,是只能輸入文字的表格
要改成可以選擇檔案的表格,重點是 4 行的 form.file.field
:
<!-- app//views/profiles/_form.html.erb -->
<div class="field">
<%= form.label :avatar %>
<%= form.file_field :avatar %>
</div>
點擊之後就會跳出選取檔案的視窗了!
既然都上傳檔案了,就要在畫面顯示出來,那就來更改show.html.erb
<!-- app/views/profiles/ -->
<p id="notice"><%= notice %></p>
<p>
<strong>Nickname:</strong>
<%= @profile.nickname %>
</p>
<p>
<strong>Email:</strong>
<%= @profile.email %>
</p>
<p>
<strong>Avatar:</strong>
<br>
<%= image_tag @profile.avatar.url if @profile.avatar.url.present? %>
</p>
<%= link_to 'Edit', edit_profile_path(@profile) %> |
<%= link_to 'Back', profiles_path %>
image_tag
是一個 helper,會自動長出 <img src="...">
,後面的判斷式用意在於,可以避免只用者沒有上傳圖檔時,不會造成抓不到資料,而發生錯誤情況。使用者有上傳圖片就顯示出來,若沒有就當作沒有這回事。
當使用者填完資料、選完檔案後,按下 submit,照片的連結就會傳送到資料庫儲存
CarrierWave 就會把檔案儲存在 public/uploads/profile/avatar
你可以打開這個資料夾,就會看到上傳的照片囉!
參考資料:
GitHub - CarrierWave
Ting's筆記 - carrierwave
PJCHENder未整理筆記
學無止盡,每天都要進步一點點!