MyFirstRails

Trixでの画像アップロード機能実装

概要

Railsにこれから初めて触れる方を対象にしたチュートリアルです

Trixに画像アップロード機能を作るチュートリアルになります

チュートリアル

Railsのひな型を作る

まず、rails newを実行し、Railsアプリのひな型を作成します

rails new trix_image

次に、作成したRailsアプリのディレクトリへと移動します

cd trix_image

SQLite3のバージョンを修正

先ほどのrails newsqlite3のインストールがエラーになっている場合は、以下のようにバージョンを修正します

gem 'sqlite3', '1.3.13'

その後、bundle installを実行します

bundle install

ScaffoldでCRUDを作成

rails g scaffold コマンドを使い、Trixで使用するCRUDを作成します

rails g scaffold post title:string content:text auther:string

次に、画像投稿用のCRUDを作成します

rails g scaffold photo image

その後、rails db:migrateでマイグレーションを行います

rails db:migrate

Trixの導入

Trixを導入していきます

まずはGemfileTrixを追加します

gem 'trix'

その後、bundle installを実行します

bundle install

次に、app/assets/javascripts/application.jsに以下の行を追加します

//= require trix

また、app/assets/stylesheets/application.cssにも以下の行を追加します

*= require trix

app/views/posts/_form.html.erbを以下のように変更します

<%= form_with(model: post, local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>

  <div class="field">
    <%= form.label :content %>
    <%= form.hidden_field :content, id: 'post_content' %>
    <trix-editor input="post_content"></trix-editor>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

最後に、app/view/posts/show.html.erbを以下のように変更します

<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<p>
  <strong>Content:</strong>
  <%= sanitize @post.content %>
</p>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

Shrineでの画像アップロード

Shrineというアップロード用のgemを使用し、画像アップロード機能のひな型を作成します

まず、GemfileShrineを追加します

gem 'shrine'

その後、bundle installを実行します

bundle install

その後、config/initializers/shrine.rbを以下のように作成します

require "shrine"
require "shrine/storage/file_system"

Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
  store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
}

Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data

次に、app/views/photos/_photo.json.jbuilderに以下の一行を追加します

json.image_url photo.image_url 

アップロード用のモデルapp/models/image_uploader.rbを作成します

class ImageUploader < Shrine
end

作成したモデルをPostモデルにincludeします

class Photo < ApplicationRecord
    include ImageUploader[:image]
end

rails g migrationコマンドを使用して既存のimageカラムをimage_dataカラムにリネームします

rails g migration RenameImageColumnToPhotos

作成したマイグレーションファイルを以下のようにします

class RenameImageColumnToPhotos < ActiveRecord::Migration[5.2]
  def change
    rename_column :photos, :image, :image_data
  end
end

rails db:migrateを実行し、マイグレーションを行います

rails db:migrate

後は、app/views/photos/_form.html.erbを編集し、アップロードができるようにします

<%= form_with(model: photo, local: true) do |form| %>
  <% if photo.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(photo.errors.count, "error") %> prohibited this photo from being saved:</h2>

      <ul>
      <% photo.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :image %>
    <%= form.file_field :image, id: :photo_image %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

これで、画像のアップロードが実行できるようになりました

Trixに画像をアップロード

app/assets/javascripts/application.jsを以下のように修正します

  
   function uploadAttachment(attachment) {

    var file = attachment.file;
    var form = new FormData;
    form.append("Content-Type", file.type);
    form.append("photo[image]", file);
  
    var xhr = new XMLHttpRequest;
    xhr.open("POST", "/photos.json", true);
    xhr.setRequestHeader("X-CSRF-Token", Rails.csrfToken());
  
    xhr.upload.onprogress = function(event) {
      var progress = event.loaded / event.total * 100;
      attachment.setUploadProgress(progress);
    }
  
    xhr.onload = function() {
      if (xhr.status === 201) {
        var data = JSON.parse(xhr.responseText);
        return attachment.setAttributes({
          url: data.image_url,
          href: data.url
        })
      }
    }
  
     return xhr.send(form);
  }
  
   // Listen for the Trix attachment event to trigger upload
  document.addEventListener("trix-attachment-add", function(event) {
    var attachment = event.attachment;
    if (attachment.file) {
      return uploadAttachment(attachment);
    }
  });

これで、エディターに画像をペーストするだけで画像のアップロードができるようになります!