2021年12月23日木曜日

drawingboard.js でサーバサイドに画像を保存する方法

drawingboard.js でサーバサイドに画像を保存する方法

概要

前回 drawingboard.js に入門してみました
今回はサーバサイドに作成した画像を保存してみます
サーバサイドは Ruby で実装します

環境

  • macOS 11.6.2
  • Ruby 3.0.3p157
  • sinatra 2.1.0
  • drawingboard.js 0.4.2

config.ru

require './app'

run TestWeb

app.rb

require 'sinatra'
require 'base64'

class TestWeb < Sinatra::Base
  get '/' do
    erb :index
  end

  post '/' do
    filename = params[:filename]
    image = params[:image]
    image = Base64.decode64(image.gsub('data:image/png;base64,', '').gsub(/\s/, '+'))
    File.open("/tmp/#{filename}.png", 'wb') do |file|
      file.write(image)
      'ok'
    end
  end
end

index.erb

<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width">
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.css">
  <style data-example="2">
  #board1 {
    width: 400px;
    height: 400px;
  }
  </style>
</head>
<body>
  <div class="board" id="board1"></div>
  <input id="filename" type="text" value="image1" />
  <button id="save">Save</button>
  <script data-example="2">
  var board1 = new DrawingBoard.Board('board1', {
    background: "#000000",
    color: "#ffffff",
    size: 30,
    fillTolerance: 150,
    controls: [
      { Size: { type: "range", min: 12, max: 42 } },
      { Navigation: { back: true, forward: true } },
      'DrawingMode',
      'Color'
    ],
    webStorage: 'local',
    droppable: true
  });
  board1.addControl('Download');
  board1.downloadImg = function() {
    var img = this.getImg();
    img = img.replace("image/png", "image/octet-stream");
    var link = document.createElement('a');
    link.download = "download.png";
    link.href = img;
    link.click();
  };
  $('#save').on('click', function(e) {
    var img = board1.getImg();
    var filename = $("#filename").val();
    $.ajax({
      type: "POST",
      url: "/",
      enctype: 'multipart/form-data',
      data: "image=" + img + '&filename=' + filename,
      success: function(msg){
        alert( "Data Saved: " + msg );
      },
      error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert("some error");
      }
    });
  });
  </script>
</body>
</html>

ちょっと解説

画像情報を送信する場合は base64 エンコードされたデータを文字列で送信します
わざわざバイナリ変換してファイルとして送信する方法もありますが文字列のほうが扱いが楽なので文字列のまま使います

base64 の文字列を送信する場合は ajax を使います
POST で multipart/form-data として送信します
こうすることで一緒にファイル名も送信できます

Ruby (sinatra) 側ではフォームデータとして画像データとファイル名を取得します
それぞれ params から文字列として取得できます
画像データは不要な文字列をエスケープしスペースをプラスに置換します

あとは Base64.decode しファイルをバイナリ形式で保存すれば OK です
実際にファイル化するのが嫌な場合は Redis などに文字列としてそのまま保存すると良いかなと思います

最後に

なぜか ajax から受け取った画像の base64 データがサーバサイドだと一部置換されているのでそこをちゃんと補完してあげることを忘れないようにしましょう

参考サイト

0 件のコメント:

コメントを投稿