0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

登録フォーム上でアップロードされた画像のプレビューを表示する(Rails)

Posted at

https://qiita.com/AttAtD/items/6b9502c0fa75531ee400
の続き

画像処理するGemの追加

  • Gemにmini_magickとimage_proceccerを追加

Gemfile

gem "image_processing", "~> 1.2"
gem 'mini_magick'

追加後にbundler installを実行

  • application.rbに使用するプロセッサーを登録

config.active_storage.variant_processor = :mini_magick

投稿画面にプレビュー表示を追加する。

  • app/views/posts/new.html.erbに表示エリアを追加

また、ファイル選択時にJavascriptのメソッド(image-preview#readImage)を呼び出すようにする。


<div data-controller="image-preview">
  <div><%= f.file_field :photos, data: { "image-preview-target": "input", action: "image-preview#readImage" }%></div>

  <ul data-image-preview-target="output">
  </ul>
</div>


  • javascripts/controller/image_preview_controller.jsを追加。
import { Controller } from "@hotwired/stimulus"

let data = null;

export default class extends Controller {
  static targets = [ "output", "input"];
  
  connect() {
    data = new DataTransfer();
  }

  readImage(e) {
    let input = this.inputTarget;
    let output = this.outputTarget;

    if (input.files) {
      let files = Array.from(input.files);
      
      files.forEach((f) => data.items.add(f));
      for (let i = 0; i < files.length; i++) {
        this.readOne(files[i], i, output);
      }
      input.files = data.files;
    }
  }

  readOne(f, index, output) {
    let reader = new FileReader();
    reader.readAsDataURL(f);
    reader.onload = () => {
      const html= `<li>
                  <div>
                    <image src="${reader.result}"/>
                  </div>
                </li>`;
      output.insertAdjacentHTML('beforeend', html);
    };
  }
}

実行

  • railsの実行
bundle exec rails s
  • http://localhost:3000/posts/new にアクセスする

以下のようにファイルを選択するたびにリストの下に画像が追加される。

Images (1)

今の実装だと、フォームの内容(タイトル)でエラーがあった場合、選択した画像がすべてクリアされる。
以下のようにDirectUploadを利用してフォーム送信時にアップロードを実行して、エラー時にその画像が読み込まれるようにする。

Postモデルにアップロードした画像をキャッシュするようにする

  • 画像のIDを表す変数(signed_ids)を追加する。
  • アップロードした画像を読み込むメソッド(set_image_cache)を追加
  • set_image_cache をバリデーション時に読み込むようにする。

class Post < ApplicationRecord
  ...

  attribute :signed_ids, default: -> { [] } # ここを追加
  before_validation :set_image_cache # ここを追加

  ...
  
   # ここを追加
  def set_image_cache
    signed_ids.each do |signed_id|
      blob = ActiveStorage::Blob.find_signed(signed_id)
      # 同じ画像を2回よみこまないようにする
      photos.attach(signed_id) unless photos.find { |image| blob.checksum == image.blob&.checksum }
    end
  end

  ...

end

  • app/views/posts/new.html.erb のfileフィールドにdirect_uploadオプションを追加
   ...
  <div>
    <%= f.file_field :photos, 
      multiple: true,
      direct_upload: true,
      data: { "image-preview-target": "input", action: "image-preview#readImage" }
    %>
  </div>
   ...
  • app/views/posts/new.html.erb の画像プレビュー部分にキャッシュされた画像の読み込みとsigned_idsの値を出力する

...
  <ul data-image-preview-target="output">
    <% @post.photos.each do |photo|%>
    <li>
      <%= f.hidden_field :signed_ids, multiple: true, value: photo.blob.signed_id %>
      <div>
        <%= image_tag photo, style: "width:300px" %>
      </div>
    </li>
    <% end %>
  </ul>
...
  • javascriptのモジュールにactivestorageを追加
% bin/importmap pin activestorage
Pinning "activestorage" to https://ga.jspm.io/npm:activestorage@5.2.8-1/app/assets/javascripts/activestorage.js
  • app/javascript/controllers/application.js に以下を追加
import { Application } from "@hotwired/stimulus"

const application = Application.start()

// Configure Stimulus development experience
application.debug = false
window.Stimulus   = application

// For direct upload
import * as ActiveStorage from "activestorage" // ここを追加
ActiveStorage.start()                                                // ここを追加

export { application }

フォーム送信時に画像がアップロードされ、エラーが発生した場合でもsigned_idsにアップロードされた画像のキーが保存されるので、エラー発生後にそれを読み込む事ができる。

0
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?