Try   HackMD

Rails專案使用ActiveStorage連接S3資料庫

Active Storage 可以支援各種後端服務(如 AWS S3),為 Active Record 模型提供檔案上傳和附件功能。

前置作業

  • 先建立一個新專案來測試
    $ rails new project_name
  • 為專案安裝Active Storage功能,會在資料庫中建立兩個名為 active_storage_blobs 和 active_storage_attachments 的資料表
    $ rails active_storage:install
  • 用scaffold建立一個用來上傳的model取名為Upload
    $ rails generate scaffold Upload
  • 將表格具現化
    $ rails db:migrate

設定ActiveStorage

  • 在upload.rb寫上,讓model有上傳功能
class Upload < ApplicationRecord
  has_one_attached :file
end
  • 在uploads_controller.rb建立create 方法
def create
@upload = Upload.new()
@upload.file.attach(params[:upload][:file])
@upload.save!

redirect_back(fallback_location: root_path)
end
  • 在config/storage.yml添加S3的設定
amazon:
  service: S3
  access_key_id: "S3的access_key_id"
  secret_access_key: "S3的secret_access_key"
  bucket: "S3貯體名稱"
  region: "S3n所在區域" # e.g. 'us-east-1'
  • 在config/environments/production.rb進行設定至amazon
    config.active_storage.service = :amazon

  • 在Gemfile加上S3套件
    gem "aws-sdk-s3", '~> 1.87', require: false

  • 安裝套件
    $ bundle install

  • 修改_form.html.erb

 <%= form_with(model: upload) do |form| %>
  <div class="form-group">
    <%= form.file_field :file %><br>
  </div>
  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>
  • 避免金要上傳,安裝fiagro
    gem 'figaro', '~> 1.0'
  • 生成需要的application.yml,這個檔案不會被上傳至github
    $ figaro install
  • 修改storage.yml中的amazon設定
amazon:
​ service: S3
​ access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
​ secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
​ bucket: <%= ENV["AWS_BUCKET"] %>
​ region: <%= ENV["AWS_REGION"] %> 
  • 將實際的金鑰寫入application.yml
amazon:
AWS_ACCESS_KEY_ID: "S3的access_key_id"  
AWS_SECRET_ACCESS_KEY: "S3的secret_access_key"
AWS_BUCKET: "S3貯體名稱"
AWS_REGION: "S3n所在區域" 
  • 將上傳功能把從本機上傳檔案改成從url儲存圖片,這裡先直接塞圖片網址來測試,修改create
def create
    require 'open-uri'
    file = open("https://oawan.me/img/program/rvm-logo-all-happy.png")
    @upload = Upload.new()
    @upload.file.attach(io: file, filename: 'some-image.jpg')
    @upload.save!
    redirect_back(fallback_location: root_path)
  end
  • 利用js抓取頁面圖片url來作為儲存目標,先在new頁面放一張圖並給予id
    <img id= "picture" src="https://oawan.me/img/program/rvm-logo-all-happy.png">
  • 在_form.html.erb,給form一個btn的class標籤,並將原本的輸入欄位改成隱藏欄位並給予一個class名稱add-src,src為儲存js抓取到的url的欄位
<div class="btn">
<%= form_with(model: upload) do |form| %>
  <div class="form-group">
    <%= form.hidden_field :src, value: "", class: 'add-src' %><br>
  </div>
  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>
 </div>
  • 為uploads增加一個名為src的新欄位

  • 在app/javascript/packs新增upload.js


window.addEventListener('turbolinks:load', function() {
  const btn = document.querySelector('.btn')
  const pic = document.getElementById('picture')
  const form = document.querySelector("form")
  let inputValue = document.querySelector('.add-src')
  console.log(inputValue)

  btn.addEventListener('click', function(){
    inputValue.setAttribute("value", pic.src)
  })
})
  • 在app/javascript/packs/application.js中引入upload.js
    import "./upload"
  • 最後因為我們要抓取網頁中的圖片來儲存,所以將create修改
def create
    require 'open-uri'
    url = params[:upload][:src]
    file = open(url)
    @upload = Upload.new(upload_params)
    @upload.file.attach(io: file, filename: 'some-image.jpg')
    @upload.save!
    redirect_back(fallback_location: root_path)
  end
  • 清洗params
private
    def upload_params
      params.require(:upload).permit(:src)
    end

部署至Heroku進行測試

  • 接著部署至Heroku,可以看到用scaffold做出的畫面
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 選擇文章的截圖上傳
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 再到Amazon S3,就可以看到檔案已經上傳
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 打開檔案來確認,確認是所上傳的截圖,到這裡基本上專案已經有上傳檔案至S3的功能
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 目前的設定只能在部署上線後傳檔案至S3,如果在開發環境也想要上傳S3,只要將/Users/ryan/ss3/config/environments/development.rb中的config.active_storage.service = :local改成config.active_storage.service = :amazon即可