[Rails] carrierwave uploader
keywords: file upload
產生 uploader
執行下面其他步驟前,要先建立 uploader
:
# create app/uploaders/fieldname_uploader.rb
rails generate uploader Avatar
上傳單一檔案
Generator
rails g migration add_avatar_to_users avatar:string
Model
在 model 中掛入 uploader
# ./app/models/user.rb
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
Controller
# ./app/controllers/users_controller.rb
def user_params
params.require(:user).permit(:name, :avatar)
end
View
在 View 中使用,這時候送出表單,就會自動將檔案上傳到 ./public/uploads/<fieldName>
資料夾中,同時將檔名存入相對應的 table field 中。
<!-- ./app/views/users/_form.html.erb -->
<%= form_with(model: user, local: true) do |form| %>
<div class="field">
<%= form.label :avatar %>
<%= form.file_field :avatar, id: :product_avatar %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
利用在 View 中可以透過:
product.avatar # 取得圖檔相對路徑
product.avatar.url # 取得圖檔相對路徑(和上面一樣)
product.avatar.current_path # 取得圖檔絕對路徑
product.avatar_identifier # 取得檔名(是用底線 _ )
product.avatar.file.nil? # 判斷檔案是否存在
注意:
product.avatar
永遠不會回傳nil
,即使沒有和它關連的圖片也一樣。可以透過product.avatar.file.nil?
來檢驗是否有相關連的圖片。
上傳多個檔案 multiple files upload
Generator
上傳多個檔案時,欄位名稱加上 s ,且欄位格式為 json
rails g migration add_avatars_to_users avatars:json
rails db:migrate
Model
原本的 mount_uploader
和 :avatar
都要變成複數:
# ./app/models/user.rb
class User < ActiveRecord::Base
mount_uploaders :avatars, AvatarUploader
end
Controller
# 這是透過 AJAX 上傳圖檔
# ./app/controllers/users_controller.rb
def update_images
add_more_images(params[:product][:photos]) unless params[:product][:photos].nil?
msg = {
:status => 'get data',
:photos => @product.photos.map(&:url)
}
render :json => msg
end
# controller
# new_images 代入 params 傳入的圖檔,例如 params[:product][:photos]
def add_more_images(new_images)
images = @product.photos
images += new_images
@product.photos = images
@product.save
end
View
file_field
要記得加上 multiple: true
:
<!-- ./app/views/users/_form.html.erb -->
<%= form_with(model: user, local: true) do |form| %>
<div class="field">
<%= form.label :avatars %>
<%= form.file_field :avatars, id: :user_avatars, multiple: true %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
透過 form.file_field
產生的 input tag 會長像下面這樣,其中 name 會是 name="user[avatars][]"
:
<input id="user_avatars" multiple="multiple" type="file" name="user[avatars][]" />
上傳後 db 的 table 中會以陣列的方式儲存["Chrome.jpeg","html5.png","JS.png"]
在 View 中可以透過下面方式來採用:
<!-- identifier 目前無法在 multiple upload 使用 -->
<%= "url:#{avatar.url}" %>
<%#= "identifier: #{avatar.identifier}" %>
<%= "current_path: #{avatar.current_path}" %>
<%= "file.nil?: #{avatar.file.nil?}" %>
移除上傳的檔案
下面這是可以透過 AJAX 的方式刪除圖檔,其中 @product.write_attribute(:photos, []) if @product.photos.empty?
這行是修復 CarrierWave 已經的 Issue,如果沒有這行,將無法刪除最後一張圖片:
# controller
def delete_images
remove_images(params[:remove_photos_url]) unless params[:remove_photos_url].nil?
msg = {
:status => 'delete data',
:photos => @product.photos.map(&:url)
}
render :json => msg
end
def remove_images(photos_url)
remain_images = @product.photos # copy the array
remain_images = remain_images.reject do |image|
photos_url == image.url
end
@product.photos = remain_images # re-assign back
@product.write_attribute(:photos, []) if @product.photos.empty? # Fix carrierwave issues when deleting last file
@product.save
end
Known Issue
參考
- CarrierWave Wiki @ github
- How to: Add more files and remove single file when using default multiple file uploads feature @ carrierwave
- Multiple Images Uploading With CarrierWave and PostgreSQL Array by Bob Cao
- Sample Gallery App with Carrier Wave @ Github by Bob Cao
- Append more files @ carrierwave github issues
- How to Upload to Carrierwave with JavaScript @ QuickLeft Blog
相關閱讀
- [JS] 檔案上傳(Input File) @ PJCHENder HackMD
- [Rails] AJAX 小技巧 @ PJCHENder HackMD