クライアントの PC にある画像を HTML5 の File API を利用して取得し、それを指定のサイズに縮小して HTML5 の canvas に描画し、canvas に描画された画像を jQuery.Ajax を使って Web サーバーにアップするサンプルを書きます。
2022/11/20 追記: ASP.NET の FileUpload コントロールや jQuery は使わないで、html と JavaScript のみを使って同様な機能を実装した記事を「canvas の画像をアップロード (その 2)」に書きました。
大まかな手順は以下の通りです。詳しくは下のサンプルコードとそれに書いたコメントを参照ください。
-
クライアントによる画像ファイルの選択は ASP.NET の FileUpload コントロール(普通の HTML の <input type="file" ... /> でも可)を利用する。
-
FileUpload コントロールで画像ファイルが選択されたタイミングで、HTML5 File API の FileReader オブジェクトに readAsDataURL メソッド を使って選択された画像ファイルを読み込む。
-
FileReader の result プロパティ を使って、読み込んだ画像ファイルを Data url 形式("data:image/jpeg;base64, ..." という文字列)で取得し、それを image オブジェクトの src 属性に設定する。
-
その image オブジェクトを HTML5 の canvas に drawImage メソッド を使って描画する。その際、描画する画像の最大サイズの制限を設け(今回のサンプルでは 500 x 500 とした)、それに入る場合はそのまま、入らない場合は幅・高さどちらか大きい方を 500px に縮小し他方をその縮小率と同じに縮小(要するに縦横比を保ったまま 500 x 500 に入るよう縮小)する。
-
canvas 上の縮小後の画像データを取得して Web サーバーに非同期で送信するメソッドを作る。canvas からの画像データの取得は toDataURL メソッド を用いる。Data url 形式で取得できるので、それを jQuery.Ajax を用いて JSON 形式で送信する。(BASE64 でエンコードされているので、バイナリ形式よりサイズが約 1.3 倍大きくなってしまうが・・・)
-
<input type="button" ... /> タイプのボタンを配置し、その onclick 属性に上記のメソッドを設定する。
-
非同期でクライアントから送信された BASE64 形式の画像データは、.aspx ページに静的メソッドを追加してそれで受け、デコードしてバイナリ形式に戻してファイルに保存する。
-
オマケとして、画像ファイルのサイズを 500,000 bytes に、ファイルのタイプを image/jpeg に制限した。
サンプルコードは以下の通りです。自分の開発環境の IE9 では File API が使えないので試してませんが、Firefox 38.0.5, Chrome 43.0.2357.130 m, Opera 12.17 で期待通り動くことは確認しました。
同様なコードを 実験室 にアップしましたので、よろしければ実際に動かして試してみてください。画像ファイルのサイズを 500,000 bytes、タイプを image/jpeg に制限しているので注意してください。(実験室ではファイルのアップロードと保存はしていません。代わりに短い文字列を応答として返すようにしています)
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services" %>
<%@ Import Namespace="System.IO" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
// jQuery.Ajax で POST された画像データを受けるためのメソッ
// ド。public static にして WebMethodAttribute 属性を付与
[WebMethod]
public static string ReceiveImage(string imgBase64)
{
// 文字列先頭の "data:image/jpeg;base64," を除去。
imgBase64 =
imgBase64.Replace("data:image/jpeg;base64,", "");
// BASE64 エンコードされた画像データを元のバイト列に変換
Byte[] imgByteArray = Convert.FromBase64String(imgBase64);
// ファイル格納フォルダ FileUploadTest の物理パスを取得
string savePath =
HttpContext.Current.Server.MapPath("~/FileUploadTest/");
// ファイル名を設定
string filename =
"img" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".jpg";
// ファイル格納フォルダ FileUploadTest に画像ファイルを保存
File.WriteAllBytes(savePath + filename, imgByteArray);
return "ファイル名 " + filename + " として保存しました。";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Canvas Image Upload</title>
<script src="Scripts/jquery-1.8.3.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
// 画像ファイルのサイズ制限を 500,000 bytes、タイプ制限を
// image/jpeg とする
var maxFileSize = 500000;
var allowedContentType = "image/jpeg";
// アップロードする画像のサイズを 500 x 500 以下とする
var maxWidth = 500;
var maxHeight = 500;
// HTML5 File API の FileReader を利用して FileUpload で
// 選択された画像ファイルを読み込み、その画像の Data url
// 形式文字列を取得。それを image オブジェクトの src 属性
// に設定する。その際 image.onload イベントが発生するので
// そのイベントにコールバック関数(リスナ)をアタッチ。そ
// のリスナの中で image オブジェクトが保持する画像をにサ
// イズ制限以下に縮小して canvas 描画する。以下の 3 行は
// そのための準備
var fileReader;
var image = new Image();
image.onload = DrawImageOnCanvas;
// document の読み込み完了後に { } 内の処置を行う
$(function () {
// ブラウザの HTML5 File API サポートを確認
if (window.File && window.FileReader && window.FileList) {
fileReader = new FileReader();
// FileReader オブジェクトに画像を読み込むメソッド
// readAsDataURL は非同期で動くので、読み込み完了の
// イベントを待ってリスナで処置する必要がある。
// readAsDataURL で読み込みが完了すると onloadend
// イベントが発生するので、それにリスナをアタッチし、
// そこで FileReader から Data url を取得し image
// オブジェクトの src 属性に設定する。
fileReader.onloadend = function () {
image.src = fileReader.result;
};
// FileUpload1 でファイルの選択が完了すると change
// イベントが発生するのでそれにリスナをアタッチし、
// そこで以下の処置を行う。
$("#<%=FileUpload1.ClientID%>").change(function () {
var fileUpload =
document.getElementById("<%=FileUpload1.ClientID%>");
// ファイルが選択されているか、選択されたファイル
// のタイプ/サイズは制限に入っているかを確認
if (ClientValidate(fileUpload) == false) {
$('#Button1').attr('style', 'display:none');
return;
}
// fileReader オブジェクトに FileUpload1 で選択
// された画像ファイルを読み込む
fileReader.readAsDataURL(fileUpload.files[0]);
});
}
else {
$("#<%=FileUpload1.ClientID%>").
attr('style', 'display:none');
$('#mycanvas').attr('style', 'display:none');
$('#result').text('File API がサポートされてません。');
}
});
// ファイルの確認のためのヘルパ関数
function ClientValidate(fileUpload) {
if (fileUpload.files[0] == null) {
alert("ファイルが未選択です。");
return false;
}
if (fileUpload.files[0].type != allowedContentType) {
alert("選択されたファイルのタイプが" +
allowedContentType + "ではありません。");
return false;
}
if (fileUpload.files[0].size > maxFileSize) {
alert("ファイルのサイズが制限の " +
maxFileSize + " バイトを超えています。");
return false;
}
return true;
}
// 上で定義した image オブジェクトの src 属性に Data url
// が設定されると発生する onload イベントのリスナ。ここ
// で image 要素から canvas を描画する。
function DrawImageOnCanvas( )
{
// オリジナル画像のサイズ
var w = image.width;
var h = image.height;
var targetW, targetH;
var context =
document.getElementById('mycanvas').getContext('2d');
if (w <= maxWidth && h <= maxHeight) {
// w, h ともに制限 maxWidth, maxHeight 以内 ⇒
// そのままのサイズで canvas に描画
$('#mycanvas').attr('width', w);
$('#mycanvas').attr('height', h);
context.drawImage(image, 0, 0);
}
else if (w < h) {
// w, h どちらかが制限オーバーで h の方が大きい ⇒
// 高さを maxHeight に縮小
targetH = maxHeight;
// 幅は高さの縮小比率で縮小
targetW = Math.floor(w * targetH / h);
$('#mycanvas').attr('width', targetW);
$('#mycanvas').attr('height', targetH);
context.drawImage(image, 0, 0, targetW, targetH);
}
else {
// w, h どちらかが制限オーバーで w の方が大きい ⇒
// 幅を maxWidth に縮小
targetW = maxWidth;
// 高さは幅の縮小比率で縮小
targetH = Math.floor(h * targetW / w);
$('#mycanvas').attr('width', targetW);
$('#mycanvas').attr('height', targetH);
context.drawImage(image, 0, 0, targetW, targetH);
}
$('#Button1').attr('style', 'display:inline-block');
}
// canvas の画像データを取得して jQuery.Ajax で送信。
// クライアントからサーバーへ送信できる JSON 文字列の
// 長さは、デフォルトで 102,400 文字に制限されている。
// 制限を越える場合 web.config の jsonSerialization
// 要素の設定によって変更が必要。
function uploadImage() {
var context =
document.getElementById('mycanvas').getContext('2d');
var url = context.canvas.toDataURL("image/jpeg");
$.ajax({
type: "POST",
url: "0118-CanvasImageUpload2.aspx/ReceiveImage",
data: '{"imgBase64":"' + url + '"}',
contentType: "application/json; charset=utf-8",
success: function (data) {
// .NET 3.5 で追加された d パラメータの処置
if (data.hasOwnProperty('d')) {
data = data.d;
}
$('#result').text(data);
},
error: function (jqXHR, textStatus, errorThrown) {
$('#result').text('textStatus: ' + textStatus +
', errorThrown: ' + errorThrown);
}
});
}
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:FileUpload ID="FileUpload1" runat="server" />
<input id="Button1" type="button" value="Upload"
style="display:none;"
onclick="javascript:uploadImage();" />
<br />
<canvas id="mycanvas" width="500" height="500"></canvas>
<div id="result"></div>
</div>
</form>
</body>
</html>
上のコメントにも書きましたが、クライアントからサーバーへ送信できる JSON 文字列の長さは、デフォルトで 102,400 文字に制限されています。制限を越える場合 web.config の jsonSerialization 要素の設定によって変更が必要となりますので注意してください。以下のように設定します。(数字 300000 は実際にあわせて変更してください)
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="300000"/>
</webServices>
</scripting>
</system.web.extensions>
</configuration>