Canvasで描画した画像を送信してサーバに保存する

  • 197
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

タイトルどおりのことをやった際の実装時メモ。

Canvasから画像を取得

2通りの方法が見つかった。

  1. Base64への変換
  2. Blobへの変換

Base64への変換

canvas.toDataURL()を呼び出すだけで取得可能であるが、
画像サイズに比例した巨大な文字列となり、変換によって元サイズより33%データ量が増える。

var base64= this.canvas.toDataURL('image/png');

取得したBase64を<image>srcに指定すれば画像表示され、
<a>hrefに指定すればリンククリックでダウンロードできてこれはこれで便利。

Blobへの変換

canvas.toBlob()を呼び出すだけで取得可能。
Blob形式で表現すれば、createObjectURL(blob)によりURL参照が取得できるので、
画像サイズに依存せずメモリ使用量を抑えられるらしい。(未確認)

これは便利と思いきやcanvas.toBlob()はFirefoxでのみサポートされており、
Firefox以外ではBase64経由でBlobを作成する方法とならざる得ない。

toBlob: function() {
    var base64 = this.canvas.toDataURL('image/png');
    // Base64からバイナリへ変換
    var bin = atob(base64.replace(/^.*,/, ''));
    var buffer = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++) {
        buffer[i] = bin.charCodeAt(i);
    }
    // Blobを作成
    var blob = new Blob([buffer.buffer], {
        type: type
    });
    return blob;
}

ちなみにサイズを確認すると、確かにBase64は33%増量していた。

type size
base64.length 132506
blob.size 99361

Serverへ送信する

クライント側は Sencha Touch 2.3 を使用。
というのも2.3から XMLHTTPRequest Level 2(XHR2)を完全にサポートしたので、
バイナリデータも送信できるはずなのでその試験を兼ねて。

サーバは勉強も兼ねてSinatraを利用。

  1. Base64のまま送信する
  2. Binaryに変換して送信する
  3. Blobに変換して送信する

1. Base64版

  • Base64は先頭のminetype(data:image/png;base64,の部分)は不要なため削除
  • サーバ側はデコードしてファイルに書き出してやれば画像として保存される

クライアント側

var base64 = this.canvas.toDataURL('image/png');
var request = {
    url: 'http://localhost:4567/base64',
    method: 'POST',
    params: {
        image: base64.replace(/^.*,/, '')
    },
    success: function (response) {
        console.log(response.responseText);
    }
};
Ext.Ajax.request(request);

サーバ側

require 'base64'

post '/base64' do
    File.open('imageBase64.png', 'wb') do|f|
        f.write(Base64.decode64(params['image']))
    end
    "OK"
end

2. Binary版

  • xhr2: trueを明示的に指定する
  • rawDataとして渡す(dataだと文字列と認識される)
  • サーバ側はbodyをそのまま書き出してやると画像として保存される

クライアント側

toBinary: function(canvas) {
    var base64 = canvas.toDataURL('image/png'),
        bin = atob(base64.replace(/^.*,/, '')),
        buffer = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++) {
        buffer[i] = bin.charCodeAt(i);
    }
    return buffer;
}

// Binaryデータを作成
var buf = toBinary();
var request = {
    url: 'http://localhost:4567/binary',
    method: 'POST',
    xhr2: true,
    rawData: buf.buffer,
    success: function (response) {
        console.log(response.responseText);
    }
};
Ext.Ajax.request(request);

サーバ側

post '/binary' do
    File.open('imageBin.png', 'wb') do|f|
        f.write(request.body.read)
    end
    "OK"
end

3. Blob版

  • Blobの場合ファイルとして扱えるので、FormData形式で送信する
  • rawDataとして渡す
  • サーバ側はアップロードされたファイルを書き出す

クライアント側

var buf = toBinary();
// Blobを作成
var blob = new Blob([buf.buffer], {
    type: 'image/png'
});

// FormDataとして送信
var fd = new FormData();
fd.append("image", blob);
var request = {
    url: 'http://localhost:4567/blob',
    method: 'POST',
    xhr2: true,
    rawData: fd,
    success: function (response) {
        console.log(response.responseText);
    }
};
Ext.Ajax.request(request);

サーバ側

post '/blob' do
    img = params[:image]
    File.open('imageBlob.png', 'wb') do|f|
        f.write(img[:tempfile].read)
    end
    "OK"
end

参考