在 Rails 使用 paperclip gem 來上傳圖片至 S3
GitHub: https://github.com/thoughtbot/paperclip
Step 1 安裝
- 首先必須先在電腦中安裝
ImageMagick
來當作 paperclip 的 Image Processor,
使用 homebrew 來安裝,在終端機下指令
$ brew install imagemagick
Step 2 使用
- 接下來就在需要加入圖片的 table 中加入欄位:
這裡可以直接使用指令
$ rails generate paperclip [your_table_name] [your_column_name]
會產生以下範例的 migration 檔 (也可以自己手動新增)
class Xxxxxx < ActiveRecord::Migration
def change
add_attachment :your_table_name, :your_column_name
end
end
- migration 檔確認無誤後記得
$ bundle exec rake db:migrate
會產生以下 4 個欄位:
<column>_file_name
2. <column>_file_size
3. <column>_content_type
4. <column>_updated_at
- 欄位新增好後記得在 Model 中加入
has_attached_file :your_column_name
,即大功告成!
範例:
class User < ActiveRecord::Base
has_attached_file :avatar
end
設定 paperclip
- 在 Model 中加入的
has_attached_file :your_column_name
後面加上針對單一檔案的設定`
e.g. 圖片儲存的樣式名稱及尺寸、url、path …等
> styles: { medium: “300x300>”, thumb: “100x100>” } → 為這圖片另儲存兩種尺寸
> default_url → 設定取得圖片的 url 網址
- 直接設定 paperclip 全部套用的統一設定,例如設定檔案上傳至 S3 或存在本地端
- 在 development.rb /production.rb 中分別設定需要的設定
e.g. 官方建議串 S3 的設定:
/config/environments/development.rb config.paperclip_defaults = {
:storage => :s3,
:s3_host_name => 'REMOVE_THIS_LINE_IF_UNNECESSARY',
:s3_credentials => {
:access_key_id => AWS_ACCESS_KEY_ID,
:secret_access_key => AWS_SECRET_ACCESS_KEY,
:s3_region => "YOUR_S3_REGION_HERE"
},
:bucket => 'S3_BUCKET_NAME'
}/config/environments/production.rb config.paperclip_defaults = {
:storage => :s3,
:preserve_files => true,
:s3_host_name => 'REMOVE_THIS_LINE_IF_UNNECESSARY',
:s3_credentials => {
:access_key_id => AWS_ACCESS_KEY_ID,
:secret_access_key => AWS_SECRET_ACCESS_KEY,
:s3_region => "YOUR_S3_REGION_HERE"
},
:bucket => 'S3_BUCKET_NAME'
}
2. 新增 config/initializers/paperclip.rb
檔案,並在此設定統一的設定
e.g. 若上面 development 和 production 是一樣的就可以騰到此
/config/environments/production.rb config.paperclip_defaults = {
:storage => :s3,
:preserve_files => true,
:s3_host_name => 'REMOVE_THIS_LINE_IF_UNNECESSARY',
:s3_credentials => {
:access_key_id => AWS_ACCESS_KEY_ID,
:secret_access_key => AWS_SECRET_ACCESS_KEY,
:s3_region => "YOUR_S3_REGION_HERE"
},
:bucket => 'S3_BUCKET_NAME'
}
驗證 validation
paperclip 有提供 validations 可以驗證 attachment 欄位
並有提供三種寫法:
- use validator
- AttachmentContentTypeValidator
- AttachmentPresenceValidator
- AttachmentSizeValidator
example:validates :avatar, attachment_presence: true
validates_with AttachmentPresenceValidator, attributes: :avatar
validates_with AttachmentSizeValidator, attributes: :avatar, less_than: 1.megabytes
2. with helper
- validates_attachment_presence
- validates_attachment_content_type
- validates_attachment_size
example:validates_attachment_presence :avatar
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
3. validates_attachment
:可將多種 validations 定義為一行
example:validates_attachment :avatar, presence: true,
content_type: “image/jpeg”,
size: { in: 0..10.kilobytes }
上傳至 S3 (CloudFront) 設定
- 設定的檔案位置可參考上面 #設定 paperclip
:storage => :s3
:預設為 :filesystem 會存在本地端:url => ':s3_alias_url'
:搭配 `:s3_host_alias` 讓 paperclip 產生的 s3 url 可以使用我們設定的 cloudfront url:s3_host_alias => 'xxx'
:輸入我們設定 cloudfront 的 host name:preserve_file => true
:避免 S3 上的檔案在 Rails object 刪掉時一起被刪掉(官方建議):s3_credentials => { :access_key => xxx,:secret_access_key => xxx,:s3_region => xxx }
:放 AWS 的敏感資料,記得不要直接寫在上面!可以使用 `figaro` 之類的 gem 另外保存:bucket => 'xxx'
:輸入 bucket 的名字:s3_permissions => 'private'
:預設是:public-read
,但若 bucket 設定是 private,這個就記得一定要加!否則會出現以下錯誤訊息
Aws::S3::Errors::AccessDenied — Access Denied:
aws-sdk-core (3.69.1) lib/seahorse/client/plugins/raise_response_errors.rb:15:in call’ aws-sdk-s3 (1.50.0) lib/aws-sdk-s3/plugins/sse_cpk.rb:22:incall’
aws-sdk-s3 (1.50.0) lib/aws-sdk-s3/plugins/dualstack.rb:26:in call’ aws-sdk-s3 (1.50.0) lib/aws-sdk-s3/plugins/accelerate.rb:35:incall’
aws-sdk-core (3.69.1) lib/aws-sdk-core/plugins/jsonvalue_converter.rb:20:in call’ aws-sdk-core (3.69.1) lib/aws-sdk-core/plugins/idempotency_token.rb:17:incall’
aws-sdk-core (3.69.1) lib/aws-sdk-core/plugins/param_converter.rb:24:in call’ aws-sdk-core (3.69.1) lib/aws-sdk-core/plugins/response_paging.rb:10:incall’ …….
更多設定可參考 官方 Doc
其他設定
- 重新命名檔案後上傳 S3
這裡使用到的是 paperclip 的 interpolates
- 在
config/initializers/paperclip.rb
中加入
Paperclip.interpolates :encoded_file_name do |attachment, style|
attachment.instance.encoded_file_name
end
2. 在設定 model/xxx.rb
中加入 #encoded_file_name 方法
e.g. 使用 sha_256 編碼 id 後作為檔案名稱
def encoded_file_name
“#{SecretKey.aes_128_encode(self.id)}”
end
3. 接著就可以在設定 path 時使用 :encoded_file_name
取代原本預設的 :filename
app/models/xxx.rbhas_attached_file :image, { path: ‘/images/:encoded_file_name’ }
Reference: