Ruby 現有的檔案上傳解決方案:
這些 RubyGem 把檔案上傳放在 Rails App 裡面處理,這些 RubyGem 提供了漂亮的語法,但不管是拆成 Uploader 還是在 Model 裡面使用 DSL,商業邏輯和檔案上傳邏輯總還是會混在一起。自己要處理好上傳檔案、檔案儲存、處理檔案等繁瑣的事物。Rails 也要耗費記憶體與時間來做這些事情。
不過有一點要提的是,這些 RubyGems 都是成熟穩定的解決方案,歷經考驗,使用者眾多,支援各大 Rails 版本,Bug 少,程式碼品質好,可定制性強。
Rails 已經有將工作抽出去處理的豐富經驗,對,就是 Microservices。那為什麼檔案上傳不可以?將檔案上傳拆分出去,改成 Server-Client 的架構:
| | | | | |
| Rails | ------- | attache | ------- | attache |
| Application | ------- | client | ------- | server |
| | | | | |
Your Application choonkeat/attache_rails choonkeat/attache
standalone server
僅需要 Rack + ImageMagick,5 分鐘馬上部署一個檔案處理 Server 到 Heroku 或者是 Digital Ocean。
在最近這篇 The Stack That Helped Medium Drive 2.6 Millennia of Reading Time 文章裡有相似的例子:
我們用 Go 重寫了圖片伺服器,使用了瀑布流的策略來處理圖片。
伺服器採用了 groupcache,groupcache 是一種 memcache 的替代方案,
可以減少重複的圖片處理工作。緩存雖存在記憶體裡,背後仍有 S3 做支援。
圖片在有需要處理的時候才會進行處理。這使得我們的設計師可以有彈性來修改,
並優化圖片的呈現方式,而無需重新調整所有圖片的大小。
attache 另一項創新之舉是實作了 Tus 協定,一個針對在不穩定網路進行檔案續傳的開放協定,attache 用一個 Middleware 實現了這個協定。
這份簡報有更多內容,就不在此一一重複。
Demo 見此:https://attache-demo.herokuapp.com
attache_rails 使用了: React、jQuery,目前支持 Bootstrap 3。
Gemfile
加入:
gem "attache_rails"
接著 bundle
安裝即可。
在 application.js
把 JavaScript Client 引入進來:
//= require attache
使用 attache 所上傳的檔案,在資料庫會以 JSON 格式儲存,要儲存 JSON 格式,加一個 photo
欄位,指定 type 為 text
即可。
使用者上傳單張照片:
rails generate migration AddPhotoPathToUsers photo:text
上傳多張照片也可以使用同一個欄位 photos
:
rails generate migration AddPhotoPathToUsers photos:text
PostgreSQL 可以使用 json
或 jsonb
:
rails generate migration AddPhotoPathToUsers photo:json
PostgreSQL 還可以做這樣強大的查詢:
User.where("photo ->> 'content_type' = ?", "image/png")
單一上傳:
class User < ActiveRecord::Base
has_one_attache :photos
end
多檔上傳:
class User < ActiveRecord::Base
has_many_attaches :photos
end
= f.file_field :photos, f.object.avatar_options("64x64#")
更多諸如 64x64#
的格式請參考:http://www.imagemagick.org/Usage/resize 。
單張照片
def user_params
params.require(:user).permit(:name, :photo, attaches_discarded: [])
end
多張照片
def user_params
params.require(:user).permit(:name, photos: [], attaches_discarded: [])
end
attaches_discarded: []
是什麼?
已經不要或是刪除的檔案(譬如刪除使用者時),檔案會自動從 attache 伺服器上刪除。只需要加入到 Strong Parameter 即可,無需做任何處理,attache_rails 已經都整合好了。
單張照片
= image_tag @user.photo_url("100x100#")
多張照片
- @user.photos_urls("200x200#").each do |url|
= image_tag url
環境變數 | 用途 |
---|---|
ATTACHE_URL |
指定 attache server 所在位置,預設是 http://localhost:9292 |
ATTACHE_UPLOAD_DURATION |
多久時間將上傳視為過期,預設是 600 秒 |
ATTACHE_SECRET_KEY |
用來加密從 Rails 到 attache server 的請求 |
- 若沒有設定
ATTACHE_SECRET_KEY
,則客戶端與伺服器端的請求不會加密,也無法設定ATTACHE_UPLOAD_DURATION
- 若有設定
ATTACHE_SECRET_KEY
了,其值必須要和 attache server 的SECRET_KEY
相同