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

More than 5 years have passed since last update.

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


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


参考