【備忘録】C#でメールの添付ファイルを一括保存するには?(MailKit編)

C#

皆さん、こんにちは!
上越市を拠点にし、「FA設備・装置開発」と「画像処理」に強い会社、NSIです!
私達は豊富な経験と専門知識で、各種業界の自動化・システム化のお手伝いをしています。

2月も終わりに近づき、気付けばもうすぐ春ですね。
それにしてももう1年の6分の1が終わっているとは…。

さて、プログラムネタではPythonを多く取り上げていましたが、久しぶりにC#の記事となります。
今回は C#を使ってメールの添付ファイルを一括保存する方法 をまとめました。
いつも通り、導入 → 実装 → 実行まで、詳しく解説していきます!
ちょっと長くなってしまいますが、お付き合い頂けると嬉しいです。

べんぞうくん
べんぞうくん

…ここだけの話、NSIではPythonよりもC#を使ったソフト開発の方が多いよ。

そもそも、C#でメールを受信できるメリットとは?

メールの添付ファイルを保存するには、まずメールを受信する必要があります。
メールを受信するにはメーラーと呼ばれる専用のソフトを使用するのが一般的です。
有名なものだと、GmailやOutlook、Thunderbird 等がそれに当たります。

ここで、メールを受信する際の仕組みを図で見てみましょう。
メーラーには「ブラウザ上で使えるもの」と「PCにインストールして使えるもの」の2種類あります。
ただし、どちらもメールサーバーからメールを取得することには変わりありません。

ブラウザ上で使えるメーラー

ブラウザを介してアクセスするので、デバイスを問わずにメールの閲覧が可能です。
ただし、ネットが使えない環境だと閲覧はできません。

PCにインストールして使えるメーラー

PCにインストールするため、ネットが使えない環境でも過去のメールの閲覧が可能※です。
ただし、端末毎にソフトをインストールする必要があります。

※メールを受信する際にネットを介すため、最新のメールは閲覧することができません。

多くのメーラーが存在する中、わざわざ自分でメーラーを作るメリットとは何か?
ズバリ、「既存のメーラーにはない機能が実装できる」というのが挙げられます。

活用事例

ここで、弊社での活用事例をご紹介。
弊社では、日々の日報をメールで送信しており、その際に各作業の工数を記録したExcelも添付して送信するのが決まりとなっています。このExcelは月末に集計するのですが、従業員一人一人のメールを開いて添付されているExcelを保存するという作業は結構手間がかかります。

…ということで、添付ファイルを一括保存するソフトを作成しました。
おかげで手作業と比べ、格段に集計スピードがアップしました。

C#でメール受信してみる

今回はメールを受信し、添付ファイルを保存する処理まで実装していきます。
記事内で作成したソフトを元に、「指定した名前の添付ファイルだけ保存する機能」や「ファイルの添付忘れをチェックする機能」なんかもあるとより使いやすくなりそうですね。

1. MailKitのインストール

今回は「MailKit」というライブラリを使用します。
まず、Windowsアプリケーション(.NET Framework)のプロジェクトを作成します。
作成後、メニューバーの ツール > NuGetパッケージマネージャー > ソリューションのNuGetパッケージの管理 を選択します。

NuGetの画面が表示されたら、参照が選択された状態で、「MailKit」と検索します。
先頭に出てきた項目を選択し、チェックを入れてインストールします。
正常にインストールできれば、準備は完了です。

インストール時に以下のエラーが出てしまった場合、.NET Frameworkのバージョンが非対応の可能性があります。
このエラーは、対象のフレームワークを変更することで解決することが可能です。

べんぞうくん
べんぞうくん

エラーを見ると、.NET Frameworkの場合、4.6.2, 4.7, 4.8のいずれかに対応しているみたいだね。

ソリューションエクスプローラー > プロパティ > アプリケーション を選択します。
「対象のフレームワーク」を変更し、再度インストールをお試しください。

2. 画面の作成

続いて画面を作成していきます。画面構成は以下の通りです。
GroupBox、Labelについては特に参照しないため、省略します。(名前はデフォルトのままで問題ありません。)

No.コントロール名名前
TextBoxtextBox_保存先
Buttonbutton_保存先
TextBoxtextBox_サーバー名
NumericUpDownnumericUpDown_ポート番号
TextBoxtextBox_ユーザー名
TextBoxtextBox_パスワード
Buttonbutton_接続確認
Buttonbutton_保存
べんぞうくん
べんぞうくん

フォントは自分の好きなものでOK!画像ではメイリオを使っているよ。

3. 処理の作成(接続確認)

メールサーバーと接続できるか確認する処理をForm1.csに作成します。
まずは、以下の関数を作成します。

/// <summary>	
/// メールサーバーへ接続する
/// </summary>
/// <returns></returns>
private bool ConnectServer(out Pop3Client client)
{
    // UIの値を取得
    string serverName = textBox_サーバー名.Text;
    int portNum = (int)numericUpDown_ポート番号.Value;
    string userName = textBox_ユーザー名.Text;
    string password = textBox_パスワード.Text;

    // 初期化
    client = new Pop3Client();

    try
    {
        // タイムアウトを設定(3秒)
        client.Timeout = 3000;

        // サーバー名からIPアドレスを取得
        IPAddress[] ipAdrs = Dns.GetHostAddresses(serverName);

        // POP3サーバーへ接続
        client.Connect(ipAdrs[0].ToString(), portNum, MailKit.Security.SecureSocketOptions.None);
        client.Authenticate(userName, password);
    }
    catch (Exception ex)
    {
        return false;
    }
    return true;
}

この関数では、メールサーバー設定の内容を元にPOP3サーバーへアクセスを行い、接続結果を返します。
注意点として、client.Connectのオプションである「MailKit.Security.SecureSocketOptions.None」の部分は使用しているメールサーバーによって異なります。

本記事の執筆時に使用しているメールサーバーはSSL/TLSに対応していないため「None」を設定していますが、使用しているメールサーバーに応じて変更してください。(下記参考)

オプション解説
NoneSSL/TLSを使用せずに接続を行う。
セキュリティがない環境や、サーバーがSSL/TLSに対応していない場合に使用される。
AutoMailKitが自動的に最適なオプションを選択して接続を行う。
SslOnConnect接続開始時からSSL/TLSを使用して接続を行う。
StartTls接続時は暗号化されず、その後STARTTLSコマンドを使用してSSL/TLS接続を行う。
StartTlsWhenAvailableサーバーがSTARTTLSをサポートしている場合のみSSL/TLSを使用して接続を行う。
サポートしていない場合はセキュリティなしで接続が継続される。

作成した関数を「接続確認」ボタンのクリックイベントで呼び出します。
クリックイベントは、Form1.csのデザイナー上で対象のボタンをクリックすることで、自動生成されます。

/// <summary>
/// メールサーバーとの接続確認
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_ConnectCheck_Click(object sender, EventArgs e)
{
    // メールサーバーへ接続後、メッセージを返す
    if (ConnectServer(out Pop3Client client))
    {
        MessageBox.Show("接続に成功しました。", "接続確認", MessageBoxButtons.OK, MessageBoxIcon.Information);
        client.Disconnect(true);
    }
    else
    {
        MessageBox.Show("接続に失敗しました。", "接続確認", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

このイベントでは、「ConnectServer」の戻り値に応じて異なるメッセージを表示します。
trueなら「接続に成功しました。」と表示後、サーバーとの接続を切断します。
falseなら「接続に失敗しました。」と表示します。(サーバーと接続できていないため、切断処理は不要です。)

「接続確認」処理はこれで完成です。早速実行してみましょう。
UIにメールサーバーの設定を入力し、接続確認をクリックします。

「接続に成功しました。」とメッセージが表示されたらOKです!
接続できなかった場合は、設定が正しいか確認し、問題なければ接続時のオプションを変更してみてください。

4. 処理の作成(保存先の指定)

続いて、添付ファイル保存先を指定するための処理をForm1.csに作成します。
「…」ボタンのクリックイベントに以下の処理を記述します。

/// <summary>
/// 保存先を指定する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_保存先_Click(object sender, EventArgs e)
{
    // フォルダ選択ダイアログ
    using (FolderBrowserDialog fbd = new FolderBrowserDialog())
    {
        // デフォルトの保存先を指定
        fbd.SelectedPath = textBox_保存先.Text;

        // ダイアログ表示
        if (fbd.ShowDialog() == DialogResult.OK)
        {
            // 指定した保存先をテキストボックスにセット
            textBox_保存先.Text = fbd.SelectedPath;
        }
    }
}

このイベントでは、フォルダ選択ダイアログを表示し、選択したフォルダを保存先としてセットしています。
「保存先の指定」処理はこれだけです。「…」ボタンをクリックして実行してみましょう。

フォルダ選択ダイアログが表示され、選択したフォルダのパスがテキストボックスにセットされればOKです。

5. 処理の作成(添付ファイルの保存)

最後に、添付ファイルを保存する処理をForm1.csに作成します。
「保存」ボタンのクリックイベントに以下の処理を記述します。

/// <summary>
/// 添付ファイルを保存する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_Save_Click(object sender, EventArgs e)
{
    int saveCount = 0;
    string saveDir = textBox_保存先.Text;
    
    // 保存先が設定されているか確認
    if(saveDir == "")
    {
        MessageBox.Show("保存先を設定してください。", "保存", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    // メールサーバーへ接続
    if(ConnectServer(out Pop3Client client))
    {
        // 保存先がなければ作成
        if(!Directory.Exists(saveDir))
            Directory.CreateDirectory(saveDir);

        // メールサーバー上のメール数を取得
        int mailCount = client.GetMessageCount();
        for (int i = 0; i < mailCount; i++)
        {
            // メールを取得
            MimeMessage msg = client.GetMessage(i);

            // 取得したメールの添付ファイルを取得
            foreach (MimeEntity attachment in msg.Attachments)
            {
                // 添付ファイル名を取得し、パスを生成
                string fileName = attachment.ContentDisposition.FileName;
                string filePath = Path.Combine(saveDir, fileName);

                // ファイルを生成し、デコードして保存
                using (FileStream stream = File.Create(filePath))
                {
                    MimePart part = (MimePart)attachment;
                    part.Content.DecodeTo(stream);
                    saveCount++;
                }
            }
        }

        // サーバーとの接続を切断
        client.Disconnect(true);

        // 結果メッセージ表示
        MessageBox.Show($"{saveCount} 件 保存しました。", "保存", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    else
    {
        MessageBox.Show("保存に失敗しました。", "保存", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

このイベントでは、メールサーバーへ接続し、サーバー上の各メールの添付ファイルを保存先に保存しています。
注意点として、使用しているメーラーで、受信したメールをサーバーからすぐ削除する設定を行っている場合、取得できるメールが存在しないため、実行結果は0件となってしまいます。
添付ファイルが存在するのに保存されない場合は、メーラーの設定をご確認ください。
さて、問題がなければ早速実行してみましょう。

添付ファイルが保存できると、保存した件数がメッセージ表示されます。
※添付ファイル名が重複している場合は上書きされ、メッセージの件数と実際の保存数が異なる場合があるため、注意してください。

べんぞうくん
べんぞうくん

上書きしたくない場合、既に同じ名前のファイルがあるか確認した上でリネームする必要がありそうだね。

最後に

今回は C#でメールの添付ファイルを一括保存する方法 をご紹介しました。
複数の添付ファイルを一括保存したい!という方はぜひご活用ください。

他にもまだまだ、C#を使ってできることはたくさんあるので、引き続きご紹介していきたいと思います!

ここまで読んでいただき、ありがとうございました。
ご質問・ご要望・ご相談などは、下記お問い合わせフォームからお気軽にご連絡ください。
http://www.net-nsi.co.jp/toiawase.html

べんぞうくん
べんぞうくん

この記事がいいねと思ったらGoodボタンを押してね~

タイトルとURLをコピーしました