The Ruby on Rails 5 Quick Practice Guide that you can use in the field introduces Active Storage as a way to upload files and attach them to your model.
It was written about how to attach a single image in a local environment. Because it's a big deal
I will write about. By the way, I'll be able to send emails using SendGrid.

Starting with Rails 5.2, ActiveStorage (https://railsguides.jp/active_storage_overview.html) is included. You can now easily upload images / videos to cloud storage services (Amazon S3, Cloudinary, etc.) and link them to the database (ActiveRecord).
Cloudinary is a cloud service that allows you to distribute and edit images and videos. Even if it's free, you can use up to 25 credits (≈25GB) per month, so I think it's enough for small-scale development.
Active Storage was introduced when you created a new Rails app. And also introduce the following Gem.
# Gemfile
gem 'image_processing'                 # (Resize etc.)For image processing
gem 'cloudinary', require: true        # Cloudinary
gem 'activestorage-cloudinary-service' #Link Cloudinary and Active Storage
gem 'active_storage_validations'       #For image file validation
% bundle install
Install Active Storage.
% rails active_storage:install
A migration file will be generated, so execute the migrate command to reflect it in the database.
% rails db:migrate
Set where to save the actual attachment file. Set attachments to be saved locally in the development environment and cloudinary in the production environment.
# config/development.rb
Rails.application.configure do
  # Store uploaded files on the local file system (see config/storage.yml for options).
  #Save the uploaded file to your local file system
  # (For options config/storage.See yml)。
  config.active_storage.service = :local
end
# config/production.rb
Rails.application.configure do
  config.active_storage.service = :cloudinary
end
: local,: cloudinary describes the details in config/storage.yml.
# config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>
cloudinary:
  service: Cloudinary
  cloud_name: <%= Rails.application.credentials.dig(:cloudinary, :cloud_name) %>
  api_key:    <%= Rails.application.credentials.dig(:cloudinary, :api_key) %>
  api_secret: <%= Rails.application.credentials.dig(:cloudinary, :api_secret) %>
Since it is convenient to display the image in the view later, I will also describe the following. enhance_image_tag: By writing true When you write = image_tag in the view, you can use the convenient extension by cloudinary.
# config/cloudinary.yml
development:
  cloud_name: <%= Rails.application.credentials.dig(:cloudinary, :cloud_name) %>
  api_key:    <%= Rails.application.credentials.dig(:cloudinary, :api_key) %>
  api_secret: <%= Rails.application.credentials.dig(:cloudinary, :api_secret) %>
  enhance_image_tag: true
  static_file_support: false
production:
  cloud_name: <%= Rails.application.credentials.dig(:cloudinary, :cloud_name) %>
  api_key:    <%= Rails.application.credentials.dig(:cloudinary, :api_key) %>
  api_secret: <%= Rails.application.credentials.dig(:cloudinary, :api_secret) %>
  enhance_image_tag: true
  static_file_support: false
test:
  cloud_name: <%= Rails.application.credentials.dig(:cloudinary, :cloud_name) %>
  api_key:    <%= Rails.application.credentials.dig(:cloudinary, :api_key) %>
  api_secret: <%= Rails.application.credentials.dig(:cloudinary, :api_secret) %>
  enhance_image_tag: true
  static_file_support: false
Here, Rails.application.credentials is a function called credentials that appeared in Rails 5.2 to manage keys.
Your important key information is encrypted and stored in config/credentials_yml.enc. You can see the encrypted information with the following command:
% rails credentials:show
To edit, run the following command:
% rails credentials:edit
The editor will start up, so edit it accordingly. Save with Command + S and close the tab with Command + W.
cloudinary:
  cloud_name: (Any name given to cloudinary)
  api_key: (15 digit number specified by cloudinary)
  api_secret: (27-digit alphanumeric symbol specified by cloudinary)
# Used as the base secret for all MessageVerifiers in Rails, 
including the one protecting cookies.
secret_key_base:
(128 hexadecimal number)
Allows you to attach image files to the Task model. In addition, we will also add validation of the attached image by activestorage-validator.
# app/models/task.rb
class Task < ApplicationRecord
  # has_one_attached :image #One attached image
  has_many_attached :images #Multiple attachments
  # activestorage-Verification of attached image by validator
  validates :images,
    content_type: %i(gif png jpg jpeg),                        #Image type
    size: { less_than_or_equal_to: 5.megabytes },              #file size
    dimension: { width: { max: 2000 }, height: { max: 2000 } } #Image size
end
Also create a view.
# app/views/task/new.html.slim
New registration of h1 task
= link_to 'List', tasks_path, class: 'ui right floated primary tertiary button'
= render partial: 'form', locals: { task: @task }
# app/views/task/_form.html.slim
= form_with model: task, class: 'ui form', local: true do |f|
  .field
    = f.label :name
    = f.text_field :name, required: true
  .field
    = f.label :description
    = f.text_area :description
  .field
    = f.label :images
    - if task.images.attached?
        - task.images.each do |image|
              = image_tag image
              / = image_tag task.image.variant(resize_to_limit: [300, 300])
              = f.check_box :image_ids, { multiple: true }, image.id, false
              = f.label "image_ids_#{image.id}"
                |  Delete image
    = f.file_field :images, 
        accept: 'image/jpg, image/jpeg, image/png, image/gif', 
        multiple: true
  = f.submit nil, class: 'ui primary button'
The following parts are responsible for displaying the image. When an image is attached, all the images are displayed by the each method.
- if task.images.attached?
    - task.images.each do |image|
          = image_tag image
In addition, a check box is provided so that unnecessary images can be deleted.
= f.check_box :image_ids, { multiple: true }, image.id, false
= f.label "image_ids_#{image.id}"
  |Delete image
Allows you to select multiple attachments and Only image files can be selected.
= f.file_field :images, 
    accept: 'image/jpg, image/jpeg, image/png, image/gif', 
    multiple: true
Create a controller.
# app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def update
    #Image deletion process
    params[:task][:image_ids]&.each do |image_id|
      @task.images.find(image_id).purge
    end
    if @task.update(task_params)
      redirect_to tasks_url, notice: "task"#{@task.name}"The has been updated."
    else
      render :edit
    end
  end
  private
  def task_params
    #When attaching one image
    # params.require(:task).permit(:name, :description, :image)
    #When attaching multiple images
    params.require(:task).permit(:name, :description, images:[])
  end
end
The update action allows you to delete the checked image. We also added an image to task_params so that we can receive attachments from the form.
This completes the attachment of multiple images using Active Storage.
The view is a bit dull, so I decided to use the Fomantic-UI card to display the image. Using css/javascript to clean the file form, it looks like this:
# app/views/task/_form.html.slim
- if task.errors.present?
    ul#error_explanation
      - task.errors.full_messages.each do |message|
        li = message
= form_with model: task, class: 'ui form', local: true do |f|
  .field
    = f.label :name
    = f.text_field :name, required: true
  .field
    = f.label :description
    = f.text_area :description
  .field
    = f.label :images
    - if task.images.attached?
      .ui.cards
        - task.images.each do |image|
          .card
            .image
              = image_tag image
              /
              / = image_tag task.image.variant(resize_to_limit: [300, 300])
            .extra.content
              .ui.checkbox
                = f.check_box :image_ids, { multiple: true }, image.id, false
                = f.label "image_ids_#{image.id}"
                  |  Delete image
    = f.file_field :images, 
        accept: 'image/jpg, image/jpeg, image/png, image/gif', 
        multiple: true, 
        id: 'embed_file_input'
  .ui.fluid.action.input.mb-3
    input#selected_filenames_display_area disabled="disabled"
      placeholder="There is no image file" type="text"
    label.ui.small.teal.left.floated.button for="embed_file_input"
      = semantic_icon('upload')
      |Image selection
  = f.submit nil, class: 'ui primary button'
css:
  input[type="file"] {
    display: none;
  }
  #selected_filenames_display_area {
    opacity: 1;
  }
javascript:
  // "embed_file_input"Get the element of ID attribute.(Image selection button)
  const input_files = document.getElementById("embed_file_input");
  //Selected file name display area
  const selected_filenames_display_area = 
    document.getElementById("selected_filenames_display_area");
  //When the value changes(When selecting a file)Event to be executed in
  input_files.onchange = function() {
    //Get a FileList object
    let file_lists = input_files.files;
    //Array for storing image file names
    let file_names = []
    for (let i = 0; i < file_lists.length; i++) {
      //Get the image file name from the File object
      file_names.push(file_lists[i].name)
    }
    //Export the image file name to the selected file name display area
    selected_filenames_display_area.value = file_names.join(', ')
  }
Add to production.rb so that you can send an email using SendGrid.
# config/environments/production.rb
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for 
# immediate delivery to raise delivery errors.
config.action_mailer.raise_delivery_errors = false
config.action_mailer.delivery_method       = :smtp
host                                       = 'taskleaf.herokuapp.com'
config.action_mailer.default_url_options   = { host: host }
ActionMailer::Base.smtp_settings           = {
  address:        'smtp.sendgrid.net',
  port:           '587',
  authentication: :plain,
  user_name:      ENV['SENDGRID_USERNAME'],
  password:       ENV['SENDGRID_PASSWORD'],
  domain:         'heroku.com',
  enable_starttls_auto: true
}
Heroku is a service that allows you to easily publish (deploy) web applications created with Ruby on Rails. Go to https://heroku.com and go to Sign up to create your own account.
You can also create web applications from a browser using the GUI (Graphical User Interface). And since it's a big deal, I also installed the Heroku Command Line Interface (CLI). You can do various things with just one command from the terminal, so it's easy once you get used to it.
% brew tap heroku/brew && brew install heroku
% cd taskleaf
% git init
Initialized empty Git repository in .git/
% git add .
% git commit -m "My first commit"
Log in to Heroku and create a web app. Name it taskleaf. (If it's already in use, give it a different name.)
% heroku login
% heroku create taskleaf
Heroku allows you to use a variety of add-ons. Postgresql for the database, For storage services, use Cloudinary, I want to use SendGrid as an email sending service, so Use the following command to add a function. Each add-on has various price plans depending on the capacity used, etc. Here, we have a free plan.
% heroku addons:create heroku-postgresql:hobby-dev
% heroku addons:create cloudinary:starter
% heroku addons:create sendgrid:starter
% heroku config:get SENDGRID_USERNAME
% heroku config:get SENDGRID_PASSWORD
Publish (deploy).
% git push heroku master
Now that we have published it, we will update the database.
% heroku run rails db:migrate
Open it in your browser and check.
% heroku open
You should be able to upload the file as you would in your local environment. Also, when you log in to the Cloudinary site (https://cloudinary.com/), You should see that you have the image file you uploaded to the Media Library.
I wrote it roughly, but I hope it helps someone.
 [Rails 5.2] How to use Active Storage --Qiita
[Rails on Docker on Heroku] Memo for managing images with Active Storage + Cloudinary
[Rails 5.2] How to use Active Storage --Qiita
[Rails on Docker on Heroku] Memo for managing images with Active Storage + Cloudinary
Recommended Posts