Resources

Summary

# Terminal
bundle add aws-sdk-s3
rails g scaffold pictures name
rails active_storage:install
yarn add @rails/activestorage

# app/javascript/application.js
import * as ActiveStorage from "@rails/activestorage"
ActiveStorage.start()

# models/picture.rb
class Picture < ApplicationRecord
  has_one_attached :image
end

# views/pictures/_form.html.erb
<div class="mb-3">
  <%= form.label :image, class: 'form-label' %>
  <%= form.file_field :image, class: 'form-control', direct_upload: true %>
</div>

# views/pictures/_picture.html.erb
<%= image_tag url_for(picture.image) if picture.image.attached? %>

# pictures_controller.rb
def picture_params
  params.require(:picture).permit(:name, :image)
end

# config/environments/production.rb
config.active_storage.service = :cloud

# config/storage.yml
# Wasabi
# cloud:
#   service: S3
#   endpoint: https://s3.wasabisys.com
#   access_key_id: <%= ENV['ACCESS_KEY_ID'] %>
#   secret_access_key: <%= ENV['SECRET_ACCESS_KEY'] %>
#   region: <%= ENV['REGION_NAME'] %>
#   bucket: <%= ENV['BUCKET_NAME'] %>

# Backblaze B2
# cloud:
#   service: S3
#   endpoint: https://YOURBUCKETNAME.s3.us-west-000.backblazeb2.com
#   access_key_id: <%= ENV['ACCESS_KEY_ID'] %>
#   secret_access_key: <%= ENV['SECRET_ACCESS_KEY'] %>
#   region: <%= ENV['REGION_NAME'] %>
#   bucket: <%= ENV['BUCKET_NAME'] %>
#   force_path_style: true

# Cloudflare R2
cloud:
  service: S3
  endpoint: https://YOURACCOUNTID.r2.cloudflarestorage.com/example
  access_key_id: <%= ENV['ACCESS_KEY_ID'] %>
  secret_access_key: <%= ENV['SECRET_ACCESS_KEY'] %>
  region: auto
  bucket: <%= ENV['BUCKET_NAME'] %>

# Backblaze B2 CLI tool
https://www.backblaze.com/b2/docs/quick_command_line.html

chmod +x b2-darwin
./b2-darwin authorize-account
./b2-darwin update-bucket --corsRules '[
    {
        "corsRuleName": "downloadFromAnyOriginWithUpload",
        "allowedOrigins": [
            "*"
        ],
        "allowedHeaders": [
            "*"
        ],
        "allowedOperations": [
            "s3_put"
        ],
        "maxAgeSeconds": 3600
    }
]' exampledr allPublic