![見出し画像](https://assets.st-note.com/production/uploads/images/20997145/rectangle_large_type_2_e5c13b35b53fdba21ed5ad919cea49d4.png?width=1200)
メール送信機能の開発(C#篇)
前書き
.Net Framework 4.5からは、MailKitというライブラリでメール送信機能を実現することが推奨されているので、今回はMailKitを使ってメール送信機能を実現してみます。
本文の対象者は下記の通りです:
■C#言語の知識のある方
■Visual Studioを使ったことのある方
■デスクトップ(windows)アプリケーション開発に関する知識のある方
プログラムの構造図
ソースコードの移植性を考慮し(将来、外のアプリでも使うかどうか分かりませんが)、メール送信機能をまとめて1つのクラス(CEmailSender)に実装します。メール送信が必要な場合、CEmailSenderクラスを作成し、SendMail()というメソッドを呼び出すだけで、メールが送信されるようにします。
クラス図は下記の通りです。
CEmailSenderクラスを使ってメールを送信するシーケンス図は下記の通りです。
上図のように、メール送信機能の全てをCEmailSenderというクラスに実装しておくことで、将来あらゆるアプリ(呼び出し側)に簡単にメール送信機能を搭載することが可能になります。
プログラミング(開発)
■必要資材
・Visual Studio 2019 Community - C#プログラミング統合環境(IDE)
※Visual Studioについては、こちらをご覧ください。
・Microsoft .Net Framework 4.5
※無い場合は、Microsoft公式ホームページでダウンロード可能
■プロジェクトの作成
メール送信機能を「部品」として作成したいので、下記のように「クラスライブラリー(DLL)」の形で実装します。
「次へ」を押して、プロジェクト名、保存ディレクトリと.NET Frameworkのバージョン(.NET Framework 4.5以降)を指定し、「作成」を押します。
こうすると、C#のプロジェクトが作成されますが、デフォルトではまだMailKitが使えません。
※MailKitは下記のようにインストールする必要があります。
□MailKitのインストール方法
下図のように、「ツール」>「NuGet Package manager」> 「Manage Nuget Packages for Solution...」を押します。
下図のように、MailKitを検索し、インストールします。
インストールが完了すると、下図のように、自動的にMailKitライブラリーが参照されるようになります。
■各クラスの実装
□CMailAddrAndNameクラス
メールアドレスと名前をペアで管理するために、両方を格納するクラスを作成します。
namespace jdr_core.EmailSender
{
/// <summary>
/// メールアドレスと名前を格納するクラス
/// </summary>
public class CMailAddrAndName
{
public string MailAddress { get; }
public string Name { get; }
public CMailAddrAndName(string mailAddress, string name)
{
this.MailAddress = mailAddress;
this.Name = name;
}
}//end class
}//end namespace
・ネームスペースは、jdr_core.EmailSenderを指定していますが、任意です。
・メールアドレスと名前を指定してインスタンスを作成するよう、引数付のコンストラクターを作成しています。
□CEmailContentTypeクラス
EMailの形式には、テキスト型とHTML型があります。今回はこの両方の形式を全部サポートするので、メール本文の形式を識別するための列挙型を作成します。
namespace jdr_core.EmailSender
{
/// <summary>
/// メール本文の型
/// </summary>
public enum CEmailContentType
{
TEXT, //テキスト型
HTML //HTML型
}//end enum
}//end namespace
□CEmailSenderクラス
・コード抜粋1
private string SMTPHost = "";
private int SMTPPort = 25; //平文:25 SSL:465
private bool isSSLConnection = false;
private string SMTPAccount = "";
private string SMTPPassword = "";
//SMTPサーアがユーザ認証を必要とするフラグ
private bool mustAuthenticate = false;
メール送信サーバに関する設定値です。
SMTPHost:メール送信サーバのホスト名です。
SMTPPort:使うポート番号です。
isSSLConnection:メール送信にSSLを使うかどうかを指定する変数です。(今現在、SSLを使わないメールサーバはないでしょう。)
SMTPAccount / SMTPPassword:メールサーバに接続するためのアカウントとパスワードです。
mustAuthenticate:ユーザ認証を必要とするかどうかの変数です。
※上記の設定値はメールサーバによって違ってくるので、自分のメールサーバの設定値を参照してください。
・コード抜粋2
/// <summary>
/// コンストラクター
/// </summary>
/// <param name="smtpHost">SMTPホスト</param>
/// <param name="smtpPort">SMTPポート</param>
/// <param name="account">アカウント</param>
/// <param name="password">パスワード</param>
/// <param name="isSSLConnect">SSL接続</param>
public CEmailSender(string smtpHost, int smtpPort, string account, string password, bool isSSLConnect = true, bool mustAuthenticate = true)
{
this.SMTPHost = smtpHost;
this.SMTPPort = smtpPort;
this.isSSLConnection = isSSLConnect;
this.SMTPAccount = account;
this.SMTPPassword = password;
this.mustAuthenticate = mustAuthenticate;
}
メールサーバの各設定値を指定してCEmailSenderのインスタンスを作成するように、引数付きのコンストラクターを作成しています。
・コード抜粋3
/// <summary>
/// メール送信
/// </summary>
/// <param name="toEmailAddrLst">宛先メールアドレス群</param>
/// <param name="ccEmailAddrLst">CCメールアドレス群</param>
/// <param name="bccEmailAddrLst">BCCメールアドレス群</param>
/// <param name="fromEmailAddr">送信者メールアドレス</param>
/// <param name="subject">題名</param>
/// <param name="content">メール本文</param>
/// <param name="attachFileLst">添付ファイルリスト</param>
/// <param name="contentType">メール本文の形式(TEXT or HTML)</param>
public /*async*/ void SendMail(List<CMailAddrAndName> toEmailAddrLst,
List<CMailAddrAndName> ccEmailAddrLst,
List<CMailAddrAndName> bccEmailAddrLst,
CMailAddrAndName fromEmailAddr,
string subject,
string content,
List<string> attachFileLst,
CEmailContentType contentType = CEmailContentType.TEXT)
{
...
}
メールを送信するメソッドです。
宛先メールアドレス、CCするメールアドレス、BCCするメールアドレスはそれぞれ複数を指定できるように、List<CMailAddrAndName>で定義しています。(複数の宛先に一括送信可能)
・コード抜粋4
TextPart textPart = null;
switch (contentType)
{
case CEmailContentType.TEXT: //テキスト型
textPart = new TextPart(MimeKit.Text.TextFormat.Plain);
break;
case CEmailContentType.HTML: //HTML型
textPart = new TextPart(MimeKit.Text.TextFormat.Html);
break;
}//end switch
テキスト型とHTML型のメール本文を構築することができます。
・コード抜粋5
if (attachFileLst == null || attachFileLst.Count == 0) //添付ファイル無し
{
msg.Body = textPart; //メール本文設定
}
else //添付ファイル有
{
Multipart multipart = new Multipart("Multipart/Mixed");
multipart.Add(textPart); //メール本文設定
foreach(string curFile in attachFileLst)
{
//添付ファイルはバイナリデータとして扱う
MimePart attachement = new MimePart("Application/Octet-Stream")
{
Content = new MimeContent(File.OpenRead(curFile)),
ContentDisposition = new ContentDisposition(),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(curFile)
}; //end new MimePart()
multipart.Add(attachement);
}//end foreach
msg.Body = multipart;
}//end if
今回は添付ファイル送信機能もサポートすることにしました。
メールに添付したいファイルパスをattachFileLstに格納して置くと(複数指定可能)、メールを一緒に送信してくれます。
※メールサーバによって、添付可能なファイルの容量がそれぞれ違います。
・コード抜粋6
using (SmtpClient client = new SmtpClient())
{
WriteDbgLog(curMethodName, "SMTPサーバへ接続中...");
client.Connect(this.SMTPHost, this.SMTPPort, true); //use SSL: true ファイル添付時必須
WriteDbgLog(curMethodName, "SMTPサーバへ接続完了。");
//SMTPサーバがユーザ認証を必要とする場合
if (this.mustAuthenticate)
{
//SMTPサーバユーザ認証
client.Authenticate(this.SMTPAccount, this.SMTPPassword);
WriteDbgLog(curMethodName, "SMTPサーバ認証完了。");
}//end if
WriteDbgLog(curMethodName, "メール送信中...");
client.Send(msg);
WriteDbgLog(curMethodName, "メール送信済み。");
client.Disconnect(true);
WriteDbgLog(curMethodName, "STMPサーバとの接続切断。");
}//end using
SmtpClientのインスタンスを生成し、Send()メソッドを呼び出し、メールを送信しています。
・CEmailSenderクラスのコード
using MailKit.Net.Smtp;
using MimeKit;
using System;
using System.Collections.Generic;
using System.IO;
namespace jdr_core.EmailSender
{
/// <summary>
/// email送信機能
///
/// FW4.5からは MailKitライブラリを推奨。
/// MailKitはとても使いやすい。FW4.5以降では、MailKitを使うべき。
/// 提供API:
/// ・SMTPクライアント メール送信
/// ・POP3クライアント メール受信
/// ・IMAP4クライアント メール管理
///
/// 例: googleのメールサーバ
///
/// string smtpHost = "smtp.gmail.com";
/// int smtpPort = 465; //←SSL 平文→ 25;
/// bool isSSLConn = true;
/// string smtpAcc = "huilingtech@gmail.com";
/// string pwd = "XXXXXXX";
///
/// CEmailSender mailSender = new CEmailSender(smtpHost, smtpPort, smtpAcc, pwd, isSSLConn);
///
/// mailSender.SendMail(toAddrLst, ccAddrLst, bccAddrLst, fromAddr, subject, content, fileLst, CEmailContentType.HTML);
/// </summary>
public class CEmailSender
{
private const string CLASS_FULL_NAME = "CEmailSender";
private string SMTPHost = "";
private int SMTPPort = 25; //平文:25 SSL:465
private bool isSSLConnection = false;
private string SMTPAccount = "";
private string SMTPPassword = "";
//SMTPサーアがユーザ認証を必要とするフラグ
private bool mustAuthenticate = false;
/// <summary>
/// DEBUGログを出力する
/// </summary>
/// <param name="curMethodName">メソッド名</param>
/// <param name="msg">ログメッセージ</param>
private void WriteDbgLog(string curMethodName, string msg)
{
CLogger.Log(CLogLevel.DEBUG, CLASS_FULL_NAME, curMethodName, msg, null);
}
/// <summary>
/// ERRORログを出力する
/// </summary>
/// <param name="curMethodName"></param>
/// <param name="msg"></param>
private void WriteErrorLog(string curMethodName, string msg)
{
CLogger.Log(CLogLevel.ERROR, CLASS_FULL_NAME, curMethodName, msg, null);
}
/// <summary>
/// ERRORログを出力する
/// </summary>
/// <param name="curMethodName"></param>
/// <param name="msg"></param>
/// <param name="cause"></param>
private void WriteErrorLog(string curMethodName, string msg, Exception cause)
{
CLogger.Log(CLogLevel.ERROR, CLASS_FULL_NAME, curMethodName, msg, cause);
}
/// <summary>
/// コンストラクター
/// </summary>
/// <param name="smtpHost">SMTPホスト</param>
/// <param name="smtpPort">SMTPポート</param>
/// <param name="account">アカウント</param>
/// <param name="password">パスワード</param>
/// <param name="isSSLConnect">SSL接続</param>
public CEmailSender(string smtpHost, int smtpPort, string account, string password, bool isSSLConnect = true, bool mustAuthenticate = true)
{
this.SMTPHost = smtpHost;
this.SMTPPort = smtpPort;
this.isSSLConnection = isSSLConnect;
this.SMTPAccount = account;
this.SMTPPassword = password;
this.mustAuthenticate = mustAuthenticate;
}
/// <summary>
/// メール送信
/// </summary>
/// <param name="toEmailAddrLst">宛先メールアドレス群</param>
/// <param name="ccEmailAddrLst">CCメールアドレス群</param>
/// <param name="bccEmailAddrLst">BCCメールアドレス群</param>
/// <param name="fromEmailAddr">送信者メールアドレス</param>
/// <param name="subject">題名</param>
/// <param name="content">メール本文</param>
/// <param name="attachFileLst">添付ファイルリスト</param>
/// <param name="contentType">メール本文の形式(TEXT or HTML)</param>
public /*async*/ void SendMail(List<CMailAddrAndName> toEmailAddrLst,
List<CMailAddrAndName> ccEmailAddrLst,
List<CMailAddrAndName> bccEmailAddrLst,
CMailAddrAndName fromEmailAddr,
string subject,
string content,
List<string> attachFileLst,
CEmailContentType contentType = CEmailContentType.TEXT)
{
string curMethodName = "SendMail()";
//宛先メールアドレス無し
if (toEmailAddrLst.Count == 0)
{
WriteErrorLog(curMethodName, "宛先メールアドレスが指定されていません。");
return;
}//end if
try
{
MimeMessage msg = new MimeMessage();
//宛先メールアドレス設定
foreach (CMailAddrAndName tmp in toEmailAddrLst)
{
msg.To.Add(new MailboxAddress(tmp.Name, tmp.MailAddress));
}//end foreach
//CCメールアドレス設定
foreach (CMailAddrAndName tmp in ccEmailAddrLst)
{
msg.Cc.Add(new MailboxAddress(tmp.Name, tmp.MailAddress));
}//end foreach
//BCCメールアドレス設定
foreach(CMailAddrAndName tmp in bccEmailAddrLst)
{
msg.Bcc.Add(new MailboxAddress(tmp.Name, tmp.MailAddress));
}//end foreach
//fromメールアドレス設定
msg.From.Add(new MailboxAddress(fromEmailAddr.Name, fromEmailAddr.MailAddress));
msg.Subject = subject;
TextPart textPart = null;
switch (contentType)
{
case CEmailContentType.TEXT: //テキスト型
textPart = new TextPart(MimeKit.Text.TextFormat.Plain);
break;
case CEmailContentType.HTML: //HTML型
textPart = new TextPart(MimeKit.Text.TextFormat.Html);
break;
}//end switch
//メール本文
textPart.Text = content;
if (attachFileLst == null || attachFileLst.Count == 0) //添付ファイル無し
{
msg.Body = textPart; //メール本文設定
}
else //添付ファイル有
{
Multipart multipart = new Multipart("Multipart/Mixed");
multipart.Add(textPart); //メール本文設定
foreach(string curFile in attachFileLst)
{
//添付ファイルはバイナリデータとして扱う
MimePart attachement = new MimePart("Application/Octet-Stream")
{
Content = new MimeContent(File.OpenRead(curFile)),
ContentDisposition = new ContentDisposition(),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(curFile)
}; //end new MimePart()
multipart.Add(attachement);
}//end foreach
msg.Body = multipart;
}//end if
using (SmtpClient client = new SmtpClient())
{
WriteDbgLog(curMethodName, "SMTPサーバへ接続中...");
client.Connect(this.SMTPHost, this.SMTPPort, true); //use SSL: true ファイル添付時必須
WriteDbgLog(curMethodName, "SMTPサーバへ接続完了。");
//SMTPサーバがユーザ認証を必要とする場合
if (this.mustAuthenticate)
{
//SMTPサーバユーザ認証
client.Authenticate(this.SMTPAccount, this.SMTPPassword);
WriteDbgLog(curMethodName, "SMTPサーバ認証完了。");
}//end if
WriteDbgLog(curMethodName, "メール送信中...");
client.Send(msg);
WriteDbgLog(curMethodName, "メール送信済み。");
client.Disconnect(true);
WriteDbgLog(curMethodName, "STMPサーバとの接続切断。");
}//end using
}
catch(Exception e)
{
WriteErrorLog(curMethodName, "例外発生", e);
throw;
}//end try
}
} // end class
} // end namespace
■ビルド
上記のプロジェクトをビルドすると、下図のように、jdr-core-csharp.dllというDLLファイルが作成されます。
※DLLファイル名は指定したプロジェクト名になります。
このDLLファイルを他のプロジェクトで参照することによって、CEmailSenderクラスを使ってメールを送信することが可能になります。
・上記のDLLを使ったテストメール
※HTML形式のメールを送信しています(h3, div, span, aタグ使用)。
※画像ファイルを4つ添付しています(因みに、画像内容は大連のオフョア開発会社の様子です。)。
■おまけ
googleのメールサーバを使う場合、セキュリティ検査が強化されたため、このままではメール送信処理が失敗してしまいます。
下図のように、googleサイトにログインし、googleアカウントの「安全性の低いアプリのアクセス」の設定をオンにすることでメール送信が可能になります。
まとめ
今回は.NET Frameworkを使って、C#プログラミング言語でメール送信機能を実装する方法について解説しました。
そして、アプリを開発する前に、先ずプログラムの設計(クラス図、シーケンス図)をしてから手お動かすことをお勧めします。クラス図は実際に作成しなくても、少なくとも頭の中で「どういうクラスを作るのか?どんな順番でどのメソッドを呼び出すのか?」のようなイメージができた後じゃないと、色々バグの原因になります。
では、バイバイ! Have a nice day!