[Ruby on Rilas]CarrierWaveを使用して、canvasの画像をBlobでDBに保存する

JavaScript

はじめに

<input type="file">で画像を送信・保存したときのCarrierWaveの動作(public/upload配下に画像を保存、カラムには画像名を保存)と同じような動作を、非同期でやりたかった。

こちらが実際に練習で作成してみたアプリ https://drawemapp.herokuapp.com/

開発環境
rails:6.0.4.4
ruby:2.7.5
carrierwave:2.2.2

実装の流れ

  1. canvasに記載されている画像からblobオブジェクトを生成
  2. FormDataオブジェクトにblobを挿入
  3. blobを挿入したFormDataを非同期で送信
  4. コントローラーでデータを受け取り、DBに保存する

HTML・CSS

はじめにcanvasを作成します。

<canvas class="drawPicture" id="js-drawPicture" width="600" height="400"></canvas>

<button id= "js-pictureUpload">アップロード</button>
.drawPicture {
  background-color: white;
  border:3px solid;
}

JavaScript

fetchAPIを使用して、データを非同期で送信しています。

let canvas = document.getElementById('js-drawPicture');
let ctx = canvas.getContext('2d');
const pictureUpload = document.getElementById('js-pictureUpload');

// クリックしたら発火
pictureUpload.addEventListener('click', async ()=> {
  // blobオブジェクトの作成
  let imageBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
  // FormDateオブジェクトの作成
  let formData = new FormData();

  // <input type="file"> のようにフィールドを追加
  // 第一引数はparamsで受け取るための名前になります。post[picture]とすることでストロングパラメータを使用して、受け取れます。
  formData.append("post[picture]", imageBlob, "picture.png");
  // CSRF対策
  const token = document.getElementsByName("csrf-token")[0].content;
  // データを送信
  let response = await fetch('/posts', {
    method: 'POST',
    headers: {'X-CSRF-Token': token},
    body: formData
  });
  // 送信後、一覧画面に移動します。
  let location =  window.location.replace('/posts')
};

toBlob(callback, type, encoderOptions)メソッドは画像データに対応するblobオブジェクトを生成し, 処理を行います。

promiseasyncawaitを使用すれば、blobオブジェクトを簡単に変数に代入できます。

// callbackに処理をまとめたバージョン
// これでも動作します
canvas.toBlob((blob)=> {
  let imageBlob = blob;
  let formData = new FormData();
  formData.append("post[picture]", imageBlob, "picture.png");
  const token = document.getElementsByName("csrf-token")[0].content;
  let response = fetch('/posts', {
    method: 'POST',
    headers: {'X-CSRF-Token': token},
    body: formData
  });

  let location = window.location.replace('/posts')
}, 'image/png');

Promiseasyncawaitfetchなどの解説をすると長くなってしまうので、省略します。下記のサイトを参考にしてください。

Promise

Controller

class PostsController < ApplicationController
  def new; end

  def index
    @posts = Post.all
  end

  # blobを受け取り、DBに保存
  def create
    @post = Post.new(post_params)
    @post.save
  end

  private

  // ストロングパラメーター
  def post_params
    params.require(:post).permit(:picture)
  end
end

まとめ

正常に保存されていたら、public/upload配下に画像が保存され、DBには画像名が保存されているはずです。

保存した画像は、carrierwaveurlメソッドで呼び出せます。

間違いがあったら、コメントしていただけると嬉しいです。

コメント

タイトルとURLをコピーしました