<< Windows 8 | スティック型のAndroid端末 >>

asmxよ、永遠に。

HTML5でCanvasを使ってお絵かきをして、その結果を画像ファイルとしてサーバーに保存することを考える。

方法としては何も目新しいものはなく、
canvas.toDataURL()で得られる画像(Base64でエンコードが掛っている)を
サーバーに送って、サーバー側でデコードして.pngファイルに落とすだけです。

Perl,PHP,ASP.NET等、何を使っても実現可能ですが、
開発寿命の近づいている、.asmxを使ってWEBサービスで処理してみます。
今ならWCFですが、メンテ等では見る機会があるかもしれないのでメモとして残しておきます。

下記に示す方法を何と言うのかわかりませんが、microsoftajax.jsを使っているので
ブラウザは何でも良く、Client側はMSのルールに縛られずに
(サーバーがIISであることは必須ですが、ASP用の宣言や定義とかは不要、)、
自由な .htmlや .js を並べることができます。
.asmxもBuildの必要はなく、ソースとしてアップできるので、簡単に修正できます。

出力ファイル名(相対パス)を fname、Base64でエンコードされたデータ
(先頭の'data:image/png;base64,'は削除済み)を datauri として、大概は次のようになります。

    [WebMethod]
    public void imgwrite(string fname, string datauri)
    {
        byte[] todecode_byte = null;
        try{
            System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
            System.Text.Decoder utf8Decode = encoder.GetDecoder();

            todecode_byte = Convert.FromBase64String(datauri);

        }catch (Exception e){
            throw new Exception("Error in imgwrite:" + e.Message);
        }

        string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
        FileStream fs = null;
        BinaryWriter bw = null;
        try{
            fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
            bw = new BinaryWriter(fs);
            bw.Write(todecode_byte);

        }catch (Exception e){
            throw new Exception("Error in imgwrite:" + e.Message);
        }finally{
            if (bw != null) bw.Close();
            if (fs != null) fs.Close();
        }
    }

これだけだとつまらないので、javascriptのみでサーバー側のファイル操作を可能にするサービス集合にします。
まずは、UploadService.asmx

<%@ WebService Language="C#" CodeBehind="UploadService.asmx.cs" Class="UploadService.UploadService" %>

続いて、UploadService.asmx.cs です。

// (c) Copyright HUNDREDSOFT Corporation 2012
//	All Rights Reserved.
//
//	NAME
//		UploadService.asmx.cs
//
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;

namespace UploadService
{
    /// <summary>
    /// imgupload()
    /// imgwrite(fname, datauri)
    /// txtwrite(fname, cname, datatxt)
    /// download(fname, uri)
    /// filecopy(fname, src)
    /// filedelete(fname)
    /// filemove(fname, src)
    /// 
    /// [fname]
    /// 出力ファイル名(相対パス)
    /// 
    /// [uri]
    /// 入力ファイル名(URLアドレス)
    /// 
    /// [src]
    /// 入力ファイル名(相対パス)
    /// 
    /// [datauri]
    /// 画像データをBase64でエンコードした文字列
    /// 
    /// [cname]
    /// 出力文字エンコード(例)
    /// "shift_jis"
    /// "EUC-JP"
    /// "UNICODE"
    /// "UTF-8"
    /// 
    /// [datatxt]
    /// 文字列
    /// 
    /// imguploadは、次のように呼び出す。
    ///  <form method='POST' enctype='multipart/form-data' action='UploadService.asmx/imgupload'>
    ///  <input type='text' name='filename'></input>
    ///  <input type='file' name='file'></input>
    ///  <input type='submit' value='imgupload'></input>
    ///  </form>
    /// 
    /// (注)imgwrite,txtwriteでは、100kを超える文字列は100k以下で分割して送信すること
    /// 
    /// </summary>
    /// 
    [WebService(Namespace = "http://localhost/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.Web.Script.Services.ScriptService]
    [ToolboxItem(false)]

    public class UploadService : System.Web.Services.WebService
    {
        // function imgupload
        // Multi-formでUploadされたバイナリファイルをサーバー上に書き出します。
        //
        [WebMethod]
        public void imgupload()
        {
            FileStream fs = null;
            try{
                HttpFileCollection hfc = HttpContext.Current.Request.Files;

                if (hfc.Count > 0){
                    string fname = HttpContext.Current.Request.Form["filename"];
                    string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");

                    Stream his = hfc[0].InputStream;
                    byte[] bdata = new byte[his.Length];
                    his.Read(bdata, 0, (int)his.Length);

                    fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
                    fs.Write(bdata, 0, bdata.Length);
                }

            }catch (Exception e){
                throw new Exception("Error in imgupload:" + e.Message);
            }finally{
                if (fs != null) fs.Close();
            }
        }

        // function imgwrite
        // Base64でエンコードされたバイナリファイルをサーバー上でデコードして書き出します。
        //
        [WebMethod]
        public void imgwrite(string fname, string datauri)
        {
            byte[] todecode_byte = null;
            try{
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                todecode_byte = Convert.FromBase64String(datauri);

            }catch (Exception e){
                throw new Exception("Error in imgwrite:" + e.Message);
            }

            string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
            FileStream fs = null;
            BinaryWriter bw = null;
            try{
                fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
                bw = new BinaryWriter(fs);
                bw.Write(todecode_byte);

            }catch (Exception e){
                throw new Exception("Error in imgwrite:" + e.Message);
            }finally{
                if (bw != null) bw.Close();
                if (fs != null) fs.Close();
            }
        }

        // function txtwrite
        // サーバー上に、様々な日本語コードでテキストファイルに出力します。
        //
        [WebMethod]
        public void txtwrite(String fname, String cname, String datatxt)
        {
            System.IO.StreamWriter sw = null;
            try{
                System.Text.Encoding enc = System.Text.Encoding.GetEncoding(cname);
                // BOMなし
                if (cname == "UTF-8"){
                    enc = new System.Text.UTF8Encoding(false);
                }else if (cname == "UNICODE"){
                    enc = new System.Text.UnicodeEncoding(false, false);
                }
                string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
                sw = new System.IO.StreamWriter(filename, true, enc);
                sw.Write(datatxt);

            }catch (Exception e){
                throw new Exception("Error in txtwrite:" + e.Message);
            }finally{
                if (sw != null) sw.Close();
            }
        }

        // function download
        // URIで指定されたファイルを、サーバー上にダウンロードします。
        //
        [WebMethod]
        public void download(string fname, string uri)
        {
            BinaryReader br = null;
            FileStream fs = null;
            BinaryWriter bw = null;
            try{
                WebClient wc = new WebClient();
                br = new BinaryReader(wc.OpenRead(uri));
                byte[] read_byte = null;
                int read_count = 0;

                string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
                fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
                bw = new BinaryWriter(fs);

                do{
                    read_byte = br.ReadBytes(1024);
                    read_count = read_byte.Length;
                    bw.Write(read_byte);
                } while (read_count == 1024);

            }catch (Exception e){
                throw new Exception("Error in download:" + e.Message);
            }finally{
                if (br != null) br.Close();
                if (bw != null) bw.Close();
                if (fs != null) fs.Close();
            }
        }

        // function filecopy, filedelete, filemove
        // サーバー上でファイルをコピー、削除、移動します。
        //
        [WebMethod]
        public void filecopy(string fname, string src)
        {
            try{
                string psrc = HttpContext.Current.Request.MapPath(src);
                string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
                File.Copy(psrc, filename, true);

            }catch (Exception e){
                throw new Exception("Error in filecopy:" + e.Message);
            }
        }

        [WebMethod]
        public void filedelete(string fname)
        {
            try{
                string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
                File.Delete(filename);

            }catch (Exception e){
                throw new Exception("Error in filedelete:" + e.Message);
            }
        }

        [WebMethod]
        public void filemove(string fname, string src)
        {
            try{
                string psrc = HttpContext.Current.Request.PhysicalApplicationPath + src.Replace("/", "\\");
                string filename = HttpContext.Current.Request.PhysicalApplicationPath + fname.Replace("/", "\\");
                File.Move(psrc, filename);

            }catch (Exception e){
                throw new Exception("Error in filemove:" + e.Message);
            }
        }

        // function getfilenames
        // サーバー上のファイル一覧を取得します。
        //
        [WebMethod]
        public string getfilenames(string src)
        {
            string ret = "";
            try{
                string psrc = HttpContext.Current.Request.PhysicalApplicationPath + src.Replace("/", "\\");
                ret = getdir(psrc, ret);

            }catch (Exception e){
                throw new Exception("Error in getfilenames:" + e.Message);
            }
            return ret;
        }

        private string getdir(string dir, string ret)
        {
            string[] files = Directory.GetFiles(dir);
            foreach (string s in files){
                ret += s + ",";
            }
            string[] dirs = Directory.GetDirectories(dir);
            foreach (string s in dirs){
                ret = getdir(s, ret);
            }
            char[] tc = {','};
            return ret.TrimEnd(tc);
        }
    }
}

最後にテスト用の test.html です。(テスト用なので,IE9(canvasサポート)だけを選ばない。)


<html>
<head>
<title>test UploadService.asmx</title>
<script type="text/javascript" src="microsoftajax.js" ></script>
<script type="text/javascript" src="uploadservice.asmx/js" ></script>
<script type="text/javascript">
function test_imgwrite()
{
	var data =
	'R0lGODlhIQAkAPcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
	'/wD//////yH+DEh1bmRyZWRzb2Z0AAAsAAAAACEAJAAACM0A/wkcSLCgwYMIEypcyLDhQn8QI0qcSHEiv4IQ+2' +
	'ncyLGjx40QLw7M2M+hQY0hCZI0WRClP5ECV7Ic6BLmP5kz/9VU6U9jToE7R/b8SJRoSqEVkyo9GnNo0acgX/L0' +
	'+TNoU6o5rd4c+lNnP6Zbsc7UinPsV6lCoao9a3Op24pt38oFG5EjSZd2nZK9ezav37xx574N/Bdv1I50+SrWy5' +
	'ctT8FuCR+ebDiq5Mp9Ke+FvPRyZswfEzMeDRXsVs5wDfJbzbq169euu8qefTAgADs=';

	UploadService.UploadService.imgwrite("save/b.gif", data, 
		function(){
			alert("OK");
		}, function(){
			alert("error");
		}
	);
}

function test_txtwrite()
{
	UploadService.UploadService.txtwrite("save/sjis.txt", "shift_jis", "abcあいうえお0123456789",
		function(){}, function(){});

	UploadService.UploadService.txtwrite("save/UNICODE.txt", "UNICODE", "abcあいうえお0123456789",
		function(){}, function(){});

	UploadService.UploadService.txtwrite("save/UTF-8.txt", "UTF-8", "abcあいうえお0123456789",
		function(){}, function(){});
}

function test_download()
{
	UploadService.UploadService.download("save/a.gif", "http://www.hundredsoft.jp/hundredsoft.gif", 
		function(){
			alert("OK");
		}, function(){
			alert("error");
		}
	);
}

function test_filecopy()
{
	UploadService.UploadService.filecopy("save/b.gif", "save/a.gif", 
		function(){
			alert("OK");
		}, function(){
			alert("error");
		}
	);
}

function test_filedelete()
{
	UploadService.UploadService.filedelete("save/b.gif", 
		function(){
			alert("OK");
		}, function(){
			alert("error");
		}
	);
}

function test_filemove()
{
	UploadService.UploadService.filemove("save/b.gif", "save/a.gif", 
		function(){
			alert("OK");
		}, function(){
			alert("error");
		}
	);
}

function test_getfilenames()
{
	UploadService.UploadService.getfilenames("save",
		function(dirstr){
			//var dirs = dirstr.split(",");
			var dirs = dirstr.replace(/,/g, "\r\n");
			alert(dirs);
		}, function(){
			alert("error");
		}
	);
}
</script>
</head>
<body>
<h3>test UploadService.asmx</h3>
<hr />
 <input type="button" id="test01button" name="test01" value="test_imgwrite" onclick="test_imgwrite()"></input>
 <input type="button" id="test02button" name="test02" value="test_txtwrite" onclick="test_txtwrite()"></input>
 <input type="button" id="test03button" name="test03" value="test_download" onclick="test_download()"></input>
 <input type="button" id="test04button" name="test04" value="test_filecopy" onclick="test_filecopy()"></input>
 <input type="button" id="test05button" name="test05" value="test_filedelete" onclick="test_filedelete()"></input>
 <input type="button" id="test06button" name="test06" value="test_filemove" onclick="test_filemove()"></input>
 <input type="button" id="test07button" name="test07" value="test_getfilenames" onclick="test_getfilenames()"></input>
<br />
<hr />
test_imgupload<br />
<form method="POST" enctype="multipart/form-data" action="UploadService.asmx/imgupload" target="idmy">
保存パス  : <input type="text" name="filename"></input><br />
画像ファイル: <input type="file" name="file"></input>
 <input type="submit" value="imgupload"></input>
</form>
<hr />
<iframe name="idmy" width="0" height="0" style="visibility:hidden"></iframe>
</body>
</html>

上記、実行環境をまとめたものを置きます。
.NET Framework V4.0が入った環境なら、App_Codeフォルダにソースだけ入れておけば良いのですが、
それ以前の環境だと、binフォルダにコンパイルしたものを入れないと、うまく動きませんでした。
   .NET V4.0以降 (App_Code)
   .NET V3.5以前 (bin)
違いはBuild済みか、そうでないかだけですので実行に違いはありません。

さて、一番問題になる環境構築について説明しますが、
作業したことがある人なら「釈迦に説法」なので、以降は適当に読み飛ばしてください。




Tags: プログラムメモ


最初に、Windows7や2008で使われている、IIS7での設定。

管理ツールの「インターネットインフォメーションサービスサービス(IIS)マネージャー」を起動します。
既定のアプリケーションプール(DefaultAppPool)を使っても良いのですが、
既存のアプリケーションに影響を与えないように、別のアプリケーションプールを作成します。

.NET4.0がインストールされている場合。


.NET4.0がインストールされていない場合。


Windowsタブレットだと、アプリケーションプール上で右クリックしても、
「アプリケーションプールの追加」が出てこない場合があります。
Windowの右の方に「アプリケーションプールの追加」がありますので、こちらをクリックして下さい。

適当なフォルダをどこかに作ります。(この例だと、P47)
そこにファイルを展開します。

公開したい、サイトの上で右クリックして「アプリケーションの追加」を選択します。



saveフォルダは外部からの読み書きやファイル一覧取得が発生するので、
saveフォルダだけはディレクトリ参照を許可します。

ここでのエイリアスがアクセスURLになります。
http://127.0.0.1/<エイリアス名>/test.html

外部PCから接続する場合は、ファイアウォールの詳細設定→受信の規則でWWWを許可して下さい。


これで動作します。
通常は必要ありませんが、親フォルダの設定によっては、saveフォルダのアクセス権が足りない場合があります。
その場合は、Usersと(必要ないかもしれないが)IIS_IUSRS,サーバーではIUSR_[PC名]、
に、フルコントロールの権限を与えてください。




IIS6での設定。
こちらは、XPや2003で設定が微妙に違いますが、大枠については変わりません。

2003では、アプリケーションプールが作成できるので、上と同様に作成した方が良いです。

zip展開後、
Webサイトの上で右クリック、新規作成で「仮想ディレクトリ」を選択します。

アクセス許可では、
・読み取り
・ASPなどのスクリプトを実行する
・ISAPIアプリケーションやCGIなどを実行する
にチェックを入れます。




ASP.NETバージョンは
.NET4.0がインストールされている場合。
4.0.xxxxxを選ぶ。

.NET4.0がインストールされていない場合。
2.0.xxxxxを選ぶ。

saveフォルダのプロパティで、書き込みとディレクトリ参照を追加する。


これで動作します。
通常は必要ありませんが、親フォルダの設定によっては、saveフォルダのアクセス権が足りない場合があります。
その場合は、Usersと(必要ないかもしれないが)IIS_IUSRS,サーバーではIUSR_[PC名]、
に、フルコントロールの権限を与えてください。

XPの場合は、アクセス許可が更に必要になります。
saveフォルダ上で右クリックして、「共有とセキュリティ」をクリック。
「ネットワーク上でこのフォルダを共有する」にチェック、
「ネットワークユーザーによるファイルの変更を許可する」にチェック
して下さい。

外部からのPCアクセスについてはいろいろあるので、一概には言えませんが、
Windows標準のファイアウォールを使っているなら、例外のポート追加でWWW(Port:80)を許可して下さい。


Port開放は、セキュリティソフトにより設定が変わるので、
まずは、localhost(127.0.0.1)でご確認ください。

author : HUNDREDSOFT | - | -