最近做專案遇到了上傳檔案的問題,想針對這個主題簡單的做個介紹。
Active Storage可以將文件上傳到Amazon S3,Google Cloud Storage或Microsoft Azure Storage等雲存儲服務,並將這些文件附加到Active Record。
它是一個可以用於開發或測試的服務,並可將文件進行備份和遷移。
其中,使用Active Storage,可以使用ImageMagick轉換圖像上傳,上傳非圖像或圖像(如PDF或影片)的檔案,並可任意從文件中存取。
by Rails Guide-Active Storage Overview
據 Rails 官方文件說明,Active Storage 是在 Rails 5.2版後才推出的功能,在它之前似乎多數人都使用 Paperclip 及 CarrierWave,這次將會已上傳圖片為例來做說明。
在開始之前,得先在終端機執行以下指令:
rails active_storage:install
rails db:migrate
第一行會產生兩個table的migration:
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
db:migrate之後,就可以看到 schema.rb 已新增兩個 table:
active_storage_blobs
: 存放附檔資訊active_storage_attachments
: 存放附檔和 model 的關聯
其中附加檔案的方式有分成一個 model 一個檔案或甚至多個檔案,我們先已有個food model舉例:
#food.rb
class Food < ApplicationRecord
has_one_attached :avatar
end
其實在schema裡是看不到avatar這個欄位,可以想成是“虛擬欄位的概念”。
如果要做個可以上傳的表單的話,可以利用 form_for來實現:
#@foods = Food.all
<%= form_for(@foods) do |form| %>
<%= form.label :avatar, '檔案上傳',
<%= form.file_field :avatar%>
#下略
<% end %>
只要是丟資料到網站上,都需要permit才可以,故要在params的地方也要記得要加上:avatar去permit。
def clean_params
params.require(:food).permit(:title, :address, :phone, :quantity, :origin_price, :discount_price, :pickup_time, :picture, :description, :endup_time, :avatar)
end
要新增檔案可以用:
food.avatar.attach(params[:avatar])
若要確定是否有附加檔案的話,可以用:
food.avatar.attached?
# 回傳boolean值
差異其實只有兩個:
改成has_many_attached
#food.rb
class Food < ApplicationRecord
#has_one_attached :avatar
has_many_attached :avatars
end
改成陣列型式avatars:[]
def clean_params
params.require(:food).permit(:title, :address, :phone, :quantity, :origin_price, :discount_price, :pickup_time, :picture, :description, :endup_time, avatars:[])
end
關於“新增檔案”及“確定是否有附加檔案”的方式是一樣的,只是記得avatar要用複數形式(avatars)
附加完檔案了,如果是照片的話要如何顯示呢?
在Rails的 Gemfile 其實有內建一個套件 mini_magick,若要做圖片顯示的效果,可以把註解拿掉 bundle 後就可以用了。
#Gemfile
# Use ActiveStorage variant
gem 'mini_magick', '~> 4.8'
<%= image_tag food.avatar.variant(resize: '300x300'), if food.avatar.attached?%>
variant
方法是用來改變上傳的圖片尺寸if food.avatar.attached?
有附加檔案的話就印出圖片。
如果沒有加上這個判斷的話,今天頁面的某個檔案若剛好沒有附加檔案,就會噴錯,原因是“food.avatar
是nil
,不能對nil
做variant
方法”。
參考資料:
Rails Guides - Active Storage Overview
Active Storage 開箱文
[Rails] Active Storage Overview
“Don’t worry about failure; you only have to be right once.”
— Drew Houston, Entrepreneur
本文同步發佈於: https://louiswuyj.tw/