LoginSignup
1
1

More than 3 years have passed since last update.

【超かんたん】Active Storageで画像投稿機能を実装しよう!

Last updated at Posted at 2021-03-04

Active Storageを利用して画像投稿機能を実装します。
今回も初心者向けにレシピ投稿アプリを例に作成していきます。

完成イメージ

teri.jpg

Active Storageとは

ファイルアップロード機能を簡単に実装できるGem。Railsガイド

事前準備

ImageMagickの導入

ImageMagickとは

ImageMagickとは画像処理ライブラリです。
コマンドラインから画像に処理を加えることができるツールです。
Homebrewからインストールするので一度構築すれば次回以降この作業は不要になります。

ターミナル
brew install imagemagick

必要なGemのインストール

MiniMagickとImageProcessingをインストールします。

MiniMagickとは

mageMagickの機能をRubyで扱えるようにするためのGem。公式ドキュメント

ImageProcessingとは

画像サイズを調整するためのGem。公式ドキュメント

Gemfile
gem 'mini_magick'
gem 'image_processing'
ターミナル
bundle install

Active Storageの導入

Active Storageをアプリケーション内で使用するために以下のコマンドを実行します。

ターミナル
rails active_storage:install

インストールが完了するとActive Storageに関連したマイグレーションが作成されるので、マイグレートします。

ターミナル
rails db:migrate

マイグレートが完了したらactive_storage_blobsテーブルとactive_storage_attachmentsテーブルが作成されているかスキーマファイルを確認しましょう。Railsガイド

db/schema.rb
#中略
ActiveRecord::Schema.define(version: 20xx_xx_xx_xxxxxx) do

  create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.bigint "byte_size", null: false
    t.string "checksum", null: false
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end
#以下略

画像を投稿しよう

アソシエーションの設定

レコードとファイルを1対1の関係で紐づけるためにhas_one_attachedメソッドを利用します。
今回はレシピ投稿アプリなのでrecipesテーブルに画像ファイルを紐付けます。

app/models/recipe.rb
class Recipe < ApplicationRecord
  has_one_attached :image
end

コントローラーの編集

画像の保存を許可するストロングパラメーターにしましょう。

app/controllers/recipes_controller.rb
class RecipesController < ApplicationController

#中略

  private
  def recipe_params
    params.require(:recipe).permit(:title, :text, :category_id, :time_required_id, :image) #「:image」を追加
  end
end

投稿画面の編集

画像を投稿するためにはfile_fieldというメソッドを使用します。Railsドキュメント

app/views/recipes/new.html.erb
<%= form_with model: @recipe, local: true do |f| %>

#中略
    <div class="form-group">
      <label class="text-secondary">画像</label><br>
      <%= f.file_field :image %>
    </div>

#以下略

<% end %>

それでは、投稿してみましょう!
new.png

保存されているか確認してみましょう。

ターミナル
rails c

[1] pry(main)> Recipe.find(1).image
   (0.4ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Recipe Load (0.2ms)  SELECT `recipes`.* FROM `recipes` WHERE `recipes`.`id` = 1 LIMIT 1
=> #<ActiveStorage::Attached::One:0x00007fc173c392e0
 @name="image",
 @record=
  #<Recipe:0x00007fc177cc2780
   id: 53,
   title: "てりやきチキン",
   text:
    "1. 鶏もも肉の両面にフォークで穴を開け、味をしみやすくします。\r\n\r\n2. ジッパー付き保存袋に鶏もも肉を入れ、調味料を加えてよく揉み込みます。\r\n\r\n3. 鶏もも肉が重ならないよう平らにならして空気を抜き、半分に折って冷凍庫に入れます。※保存期間は2週間です。\r\n\r\n4. フライパンにサラダ油を引いて中火に熱し、冷蔵庫で半解凍した鶏肉を、皮目を下にして入れます。フタをして5分蒸し焼きにします。裏返し、フタをしてさらに3分焼いたら完成です!",
   category_id: 5,
   time_required_id: 2,
   created_at: Wed, 03 Mar 2021 13:54:36 UTC +00:00,
   updated_at: Wed, 03 Mar 2021 13:54:36 UTC +00:00>>

投稿した画像を表示しよう

コントローラーの編集

app/controllers/recipes_controller.rb
class RecipesController < ApplicationController

#中略

  def show
    @recipe = Recipe.find(params[:id])
  end

  def create
    @recipe = Recipe.new(recipe_params)
    if @recipe.save
      redirect_to recipe_path(@recipe) #リダイレクト先をshowに変更
    else
      render :new
    end
  end

#以下略

end

ビューファイルの作成

保存した画像を表示するにはimage_tagメソッドを使います。Railsドキュメント
基本的な使い方は以下のとおりです。

<%= image_tag 画像ファイルのパス %>

#app/assets/imagesにcooking.jpgを配置している場合
<%= image_tag 'cooking.jpg' %>

#モデルから画像ファイルを呼び出す場合
<%= image_tag @recipe.image %>

それではビューファイルを作成していきましょう。

ターミナル
touch app/views/recipes/show.html.erb

show.html.erbを以下のように編集します。

app/views/recipes/show.html.erb
<div class="text-center">
  <div class="card recipe-card ">
    <%= image_tag @recipe.image, class: "card-img-top" %>
    <div class="card-body">
      <div class="recipe-name">
        <%= @recipe.title %>
      </div>
      <div class="recipe-content">
        カテゴリー: <span class="recipe-category"><%= @recipe.category.name %></span>
        所要時間: <span class="recipe-time"><%= @recipe.time_required.name %></span>
      </div>
      <hr>
      <p class="card-text">
        <div class="recipe-title">作り方</div>
        <div class="recipe-text d-flex justify-content-start">
          <%= safe_join(@recipe.text.split("\n"),tag(:br)) %>
        </div>
      </p>
    </div>
  </div>
</div>

しかし、このままでは画像が存在しない場合でも、画像を表示する記述が読み込まれて、エラーを引き起こしてしまいます。
この問題を解決するためにはattached?
メソッド
を使います。

attached?メソッドとは
ファイルが添付されていればtrue、添付されていなければfalseを返してくれるメソッドです。Railsガイド

それでは、ビューファイルの修正をしましょう。

app/views/recipes/show.html.erb
#中略
<div class="card recipe-card ">
  <%= image_tag @recipe.image, class: "card-img-top" if @recipe.image.attached?%>
  <div class="card-body">
#以下略

以上で完成になります。
実際に投稿が表示されるか確認してみましょう。
35752e8fc8e7033eb1122f524ce04b35.gif

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1