VS2008 WebDeveloper ExpressEditionでMainForm 本文へジャンプ
質問はこちらから→
添付ファイル送信機能付きMail Form
※注意
 ウィルスを送られる可能性や
 大きなファイルを送られてサーバが落ちる可能性などがあります。
 不特定多数に公開するWebサーバ上のフォームには向かないかもしれません。
 公開は自己責任で!!
1. さて、添付ファイル送信機能付きメールフォームを作ってみましょう。
前回の続きで作っていきます。

まず、ここで大変大きな問題があります。
添付ファイルの情報はパネル間で保存されないのです。
そのため、データを引き継ぐための仕掛けが必要になります。

仕掛け1:メールフォームに仕込む
      利点:サーバにゴミが残らない
      欠点:ダウンロード、アップロード量が増える
仕掛け2:サーバに保存する
      利点:ダウンロード、アップロード量が減る
      欠点:送信せずに中断された場合、サーバにゴミが残る

うーん、どちらも微妙にいただけない。
では、こういうのはどうか?
解決策1:確認画面を使わない
      ・・・微妙
解決策2:確認画面に添付ファイルのダイアログをつける
      無難かな?

ということで、ここは勉強と割り切り、解決策2と、一応、仕掛け1をやってみます。

では、MailFormAppのDefault.aspxの「ソース」におまじないを入れていきます。
<form id="form1" runat="server">
と書かれた部分に”enctype="multipart/form-data"”とおまじないを入れます。
<form id="form1" runat="server" enctype="multipart/form-data">
これで、データを送れるフォームになりました。
次に、「デザイン」ビューで”FileUpload”コントロールを追加します。
このコントロールは非常に古いブラウザでは動きませんが、IE3.02から大丈夫なので、現在使えない人はほとんどいないでしょう。
2. 解決策2:
行を一行追加して、”4.添付ファイル”、”確認画面で添付”などとしておけばよいでしょう。
確認フォームにも行を一行追加して、”4.添付ファイル”とし、”FileUpload”コントロールをつけておいてください。

今回のフォームでは、添付ファイルを一つしか送れませんが、”FileUpload”コントロールを複数つけることで、複数ファイルに対応可能です。

続いて、添付ファイルを受け取る部分を作り込みます。
今回は、「送信」ボタンを押したときの動作に追加します。
「送信」ボタンをダブルクリックしてください。

”Default.aspx.cs”のソースが開いたら、
protected void Button1_Click(object sender, EventArgs e)
{
などとなっているところに、
HttpPostedFile postedFimeObject = Request.Files[0];
と書かきます。これで、添付ファイルが選ばれていれば、ここに格納されます。
格納されているか確認する方法は、
if (postedFimeObject.FileName != "")
でいいと思います。個人的には、まどろっこしく、
if (postedFimeObject.FileName.Trim().Length > 0)
と書くのが好きです。場合によっては、とても重たくなります。(^^;

これを、メッセージを作成している部分
myMessage.Body = mailMessage;
の次の行などに入れます。
そして、添付ファイルがあれば、添付します。
if (postedFimeObject.FileName != "")
myMessage.Attachments.Add(new Attachment(postedFimeObject.InputStream, System.IO.Path.GetFileName(postedFimeObject.FileName), postedFimeObject.ContentType));
System.IO.Path.GetFileName()メソッドは、フルパスのファイル名などからファイル名部分だけを取り出すものです。

なんと、もう完成です。


3. 仕掛け1:
行を一行追加して、”4.添付ファイル”とし、”FileUpload”コントロールをつけてください。
確認フォームにも行を一行追加して、”4.添付ファイル”とし、”Label”コントロールをつけておいてください。
また、”HiddenField”コントロールも”2つ”つけておいてください。

今回のフォームでは、添付ファイルを一つしか送れませんが、送信ボタンとアップロードボタンを分けたり、データグリッドコントロールやリストボタンによる削除機能をつけるなど工夫することで、複数ファイルに対応可能です。(大変複雑になるので、おすすめはしません。)

続いて、添付ファイルを受け取る部分を作り込みます。
今回は、「確認画面へ」ボタンを押したときの動作に追加します。
「確認画面へ」ボタンをダブルクリックしてください。

”Default.aspx.cs”のソースが開いたら、まず、先頭の方に移動します。
protected void Button2_Click(object sender, EventArgs e)
{
などとなっているところに、
HttpPostedFile postedFimeObject = Request.Files[0];
と書かきます。これで、添付ファイルが選ばれていれば、ここに格納されます。
格納されているか確認する方法は、
if (postedFimeObject.FileName != "")
でいいと思います。個人的には、まどろっこしく、
if (postedFimeObject.FileName.Trim().Length > 0)
と書くのが好きです。場合によっては、とても重たくなります。(^^;
これを、Labelの内容を編集している最後に追加します。
Label3.Text = Server.HtmlEncode(TextBox3.Text).Replace("\n", "<br />");
の次の行などに入れます。
そして、添付ファイルがあれば、添付ファイル名を表示します。
if (postedFimeObject.FileName != "")
{
Label4.Text = System.IO.Path.GetFileName(postedFimeObject.FileName);
}
else
Label4.Text = "";
System.IO.Path.GetFileName()メソッドは、フルパスのファイル名などからファイル名部分だけを取り出すものです。

ここからが面倒な部分です。
添付ファイルの情報を不可視属性のテキストに変換して埋め込みます。
私も初めての試みです。
まず、ファイルストリーム全体をbyte列に読み込みます。
そのバイト列をBase64エンコードしてHiddenField1に入れます。
続いて、ContentTypeもHiddenField2に格納します。

このコードは、
byte[] cByteStream = new byte[postedFimeObject.ContentLength];
postedFimeObject.InputStream.Read(cByteStream, 0, postedFimeObject.ContentLength);
HiddenField1.Value = Convert.ToBase64String(cByteStream);
HiddenField2.Value = postedFimeObject.ContentType;
となります。
もっとスマートな書き方もあるかもしれませんが、ここはご容赦をm(_ _)m
これを、先ほどの”}else”の前に入れます。
”else”の後ろも、念のため、
else
{
Label4.Text = "";
HiddenField1.Value = "";
HiddenField2.Value = "";
}
と書いておきましょう。
これは、行ったり来たりしたときに少しでも軽くするためです。

今度は、送信ボタンをいじります。
送信ボタンをダブルクリックしたら、
protected void Button1_Click(object sender, EventArgs e)
{
などとなっているところに、ファイルを挿入する処理を追加します。
まず、添付ファイルがあったか確認し、
if (Label4.Text != "")
{
添付ファイルがあったら、Base64エンコードされているものをデコードし、添付します。
コードは、メッセージを作成している部分
myMessage.Body = mailMessage;
の次の行などに入れます。
System.IO.MemoryStream postedFileStream = new System.IO.MemoryStream(Convert.FromBase64String(HiddenField1.Value));
myMessage.Attachments.Add(new Attachment((System.IO.Stream)postedFileStream, Label4.Text, HiddenField2.Value));
}
ファイルストリームを作成したくないので、メモリーストリームにでコード結果を保存して、”(System.IO.Stream)”として扱うように宣言して送信する様にします。

なんとかかんとか完成です。

4. もっとうまい方法があれば教えてください。
これでも、試行錯誤の結果なので、(^^;

さて、ファイルサイズを制限したい場合ですが、
Web.configファイルを修正します。

Web.config内の<configuration><system.web>セクション内に一行追加してください。
<configuration>
  <system.web>
    <httpRuntime maxRequestLength="100" executionTimeout="120"/>
  </system.web>
</configuration>
追加するのは斜体の行です。
”maxRequestLength”は「kByte」単位です。例では100kByteです。
”executionTimeout”は秒単位です。例では2分です。
もちろん、どちらかのみの指定でもOKです。
すでに<httpRuntime>がある場合は、属性の修正か追加を行ってください。

特定のWebページだけ制限したい場合は、
<configuration>セクション内にページ用のセクションを追加します。
<configuration>
  <location path="Default.aspx">
    <system.web>
      <httpRuntime maxRequestLength="100"/>
    </system.web>
  </location>
</configuration>
といった具合に書き加えます。

これで、容量がおおよそ制限できるはずです。
注意点としては、長い文章も制限されるので、値には気を付けてください。

ファイルの種類を制限したい場合は、ファイル名を取得したのと同じように
”System.IO.Path.GetExtension()”メソッドを使って拡張子を取得して、
”.ToLower()”メソッドで小文字に統一してから、”Swtch”、”Case”で処理するのがよいでしょう。
5. 今回のメールフォームの動作は”jpg”ファイルで行っています。
また、メモリーリークを防ぐためのディスポウズも行っていません。
必要に応じて追加してください。

次回は最終回、設定を外部に出すです。
6. ここまでのプロジェクトを用意しました。
参考にされたい方はお持ちください。
解決策2:のプロジェクト MailFormAppType01.zip へのリンク
仕掛け1:のプロジェクト MailFormAppType02.zip へのリンク