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
にアクセスする
以下のようにファイルを選択するたびにリストの下に画像が追加される。
今の実装だと、フォームの内容(タイトル)でエラーがあった場合、選択した画像がすべてクリアされる。
以下のように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にアップロードされた画像のキーが保存されるので、エラー発生後にそれを読み込む事ができる。