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
Recommended Posts