Rails 與 fog-aws 與 carrierwave 打一套上傳組合拳

記錄使用 carrierwave 搭配 fog-aws 將圖片上傳到 s3 將靜態資源外部化

Rails 與 fog-aws 與 carrierwave 打一套上傳組合拳
Photo by Pickawood / Unsplash

上傳檔案到外部空間是一個很基本的需求,好處有很多例如,轉移主機時只要把程式碼打包帶走,或是礙於主機限制容量大小,又或是像 Heroku 不提供存放空間等。
那為何我們不使用 fog 反而使用 fog-aws 因為 fog 裡面有太多的 SDK 依賴了,如果整個系統只有一個上傳空間,只需要使用對應的 gem 就好了,像是 fog 的 readme 自己也有說:

If there's a metagem available for your cloud provider, e.g. fog-aws, you should be using it instead of requiring the full fog collection to avoid unnecessary dependencies.

AWS 權限與建立

  • 建立一個 s3 bucket
    記得 Object Ownership 要選 ACLs enabled
    CleanShot 2024-09-08 at 17.03.00@2x.png

  • 把 bucket 設為公開
    CleanShot 2024-09-08 at 17.05.04@2x.png

  • 建立一個 IAM 專門用來上傳
    這個是我的 policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::YOUR_BUCKET",
                "arn:aws:s3:::YOUR_BUCKET/*"
            ]
        }
    ]
}
  • 取得 Access ID 跟 key

安裝 gem

gem 'fog-aws'
gem 'carrierwave'

版本可以自己決定是否要指定,沒意外設定方式應該是不會變動才對
接著建立一個 config/initializers/carrierwave.rb

# frozen_string_literal: true

CarrierWave.configure do |config|
  if Rails.env.production? || Rails.env.staging?
    config.fog_provider = 'fog/aws'
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID') || 'ABCDE',
      aws_secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY') || 'ABCDE',
      region: 'ap-northeast-1'
    }
    config.fog_directory  = ENV.fetch('AWS_BUCKET') || 123
}
  else
    config.storage = :file
  end
end

我這邊是讓開發本地把上傳圖片丟進本地 file 省的開發浪費空間資源。

接著就可以去設定我們的 uploader 例如:

class ImageUploader < CarrierWave::Uploader::Base
  # ...
  # Choose what kind of storage to use for this uploader:
  if Rails.env.production? || Rails.env.staging?
    storage :fog
  else
    storage :file
  end
  # ...
end

如果你的設定都正確的話,ImageUploader 就會把資源丟去 s3 上去了。