一個簡易郵件群發軟件設計與實現
阿新 • • 發佈:2019-08-01
1 需求概述
指定一批郵箱地址,使用指定的郵箱傳送指定的內容。
2 功能需求
- 配置檔案配置用於傳送的郵箱資訊
- 郵件傳送功能
- 日誌視窗輸出顯示
3 介面介面
- 郵件列表框
- 標題內容輸入框
- 傳送按鈕
- 日誌輸出框
4 技術選型
.Net 4.0 C# Winform
5 實現
5.1 新建專案
- 專案命名為 SimpleEmailSender
5.2 在專案中新增配置檔案
- 配置發件郵箱資訊
<configuration> <appSettings> <add key="email_stmp" value="smtp.****.com"/> <add key="send_user_email" value="****@****"/> <add key="send_user_pass" value="密碼"/> <add key="send_user_disp" value="發件人暱稱" /> </appSettings> </configuration>
5.3 製作介面
根據介面介面需求,介面佈局如下:
5.4 郵件傳送輔助類
首先完成輔助類開發,最後再跟介面對接完成流程。
因為讀取了配置檔案,需要新增 System.Configuration 程式集的引用。
定義 MailHelper 輔助類,讀取配置引數,向外提供傳送郵件功能方法 SendMail。
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net.Mail; using System.Text; namespace SimpleEmailSender { public class MailHelper { public static String EMAIL_USERNAME = ConfigurationManager.AppSettings["send_user_email"]; public static String EMAIL_DISPNAME = ConfigurationManager.AppSettings["send_user_disp"]; public static String EMAIL_PASSWORD = ConfigurationManager.AppSettings["send_user_pass"]; public static String EMAIL_SMTP = ConfigurationManager.AppSettings["email_stmp"]; public static ValidateResult SendMail(string email, string name, string content) { return SendMail("系統訊息", email, name, content); } /// <summary> /// 傳送郵件 /// </summary> /// <param name="title">郵件標題</param> /// <param name="email">收件人地址</param> /// <param name="name">收件人名稱</param> /// <param name="content">郵件內容</param> public static ValidateResult SendMail(string title, string email, string name, string content) { MailAddress from = new MailAddress(EMAIL_USERNAME, EMAIL_DISPNAME); //郵件的發件人 MailMessage mail = new MailMessage(); //設定郵件的標題 mail.Subject = title; //設定郵件的發件人 //Pass:如果不想顯示自己的郵箱地址,這裡可以填符合mail格式的任意名稱,真正發mail的使用者不在這裡設定,這個僅僅只做顯示用 mail.From = from; //設定郵件的收件人 mail.To.Add(new MailAddress(email, name)); //設定郵件的內容 mail.Body = content; //設定郵件的格式 mail.BodyEncoding = System.Text.Encoding.UTF8; mail.IsBodyHtml = true; //設定郵件的傳送級別 mail.Priority = MailPriority.Normal; mail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess; SmtpClient client = new SmtpClient(); //設定用於 SMTP 事務的主機的名稱,填IP地址也可以了 client.Host = EMAIL_SMTP; //設定用於 SMTP 事務的埠,預設的是 25 client.Port = 25; client.UseDefaultCredentials = false; //這裡才是真正的郵箱登陸名和密碼 client.Credentials = new System.Net.NetworkCredential(EMAIL_USERNAME, EMAIL_PASSWORD); client.DeliveryMethod = SmtpDeliveryMethod.Network; //都定義完了,正式傳送了,很是簡單吧! ValidateResult vr = new ValidateResult(true, "傳送成功!"); try { client.Send(mail); return vr; } catch (Exception e) { vr.IsValid = false; vr.Message = e.Message; return vr; } } } public class ValidateResult { public bool IsValid { get; set; } public string Message { get; set; } public ValidateResult() { } public ValidateResult(bool v, string m) { IsValid = v; Message = m; } } }
5.5 清單解析
對於郵箱列表,使用正則表示式從文字框中匹配郵箱形成 List
/// <summary> /// 提取郵件列表 /// </summary> /// <param name="mails"></param> /// <returns></returns> private List<string> ParseEmailList(string mails) { List<string> list = new List<string>(); var mc = Regex.Matches(mails, @"\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+", RegexOptions.IgnoreCase); foreach (Match c in mc) { list.Add(c.Value); } return list; }
5.6 日誌輸出方法準備
在日誌框中輸出內容,為了能線上程中呼叫,使用了 Invoke 方式執行。
/// <summary>
/// 日誌輸出支援執行緒中執行
/// </summary>
/// <param name="message"></param>
private void Log(string message)
{
Invoke(new MethodInvoker(delegate
{
txtLog.AppendText(message + "\r\n");
}));
}
5.7 執行緒傳送
- 傳送方案設計
基本描述:給定郵箱列表,標題與內容,以執行緒方式執行傳送,給出執行統計與狀態。
具體實現:使用執行緒池,但一組做為一個任務,全部完成才接收下一個任務,通過完成數量與郵箱列表長度的比較來判斷是否全部完成,資訊通過日誌輸出的方式檢視,形式上通過回撥將日誌資訊傳遞給呼叫者。
為此,這裡專門定義一個傳送器,在應用中,定義一個例項來發起任務。儘管只定義一個例項,但這裡並不需要定義為設計模式中的單例模式,事實上,它是可以多例項執行。具體程式碼說話!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace SimpleEmailSender
{
public class EmailSender
{
#region 執行時資料
// 郵箱列表
private List<string> _EmailList = new List<string>();
// 完成數量
private volatile int _OverCount = 0;
// 郵件標題
private string _Title;
// 郵件內容
private string _Content;
// 完成回撥(主要是為了寫日誌)
private Action<string> _Callback;
#endregion
/// <summary>
/// 是否全部完成
/// </summary>
/// <returns></returns>
public bool IsOver()
{
return _OverCount == _EmailList.Count;
}
/// <summary>
/// 發起任務(如果不符合發起條件,則返回 false)
/// </summary>
/// <param name="emails"></param>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="callback"></param>
/// <returns></returns>
public bool Send(List<string> emails, string title, string content, Action<string> callback)
{
if (!IsOver())
{
return false;
}
_EmailList = emails;
_OverCount = 0;
_Title = title;
_Content = content;
_Callback = callback;
Start();
return true;
}
/// <summary>
/// 啟動任務
///
/// 以執行緒池方式執行,每個郵箱不論成敗完成數加1,並回調通知。
/// </summary>
private void Start()
{
foreach (string email in _EmailList)
{
var _email = email;
ThreadPool.QueueUserWorkItem(t =>
{
var vr = MailHelper.SendMail(_Title, _email, "", _Content);
_OverCount++;
_Callback(String.Format("進度[{3}/{4}] {0} 傳送 {1},返回:{2}", _email, vr.IsValid ? "成功" : "失敗", vr.Message, _OverCount, _EmailList.Count));
});
}
}
}
}
5.8 按鈕事件
傳送按鈕執行的流程為:如果之前的任務尚未完成,則等待。否則,首先提取郵箱列表,並格式化顯示,然後發起任務,觀察日誌即可。
private EmailSender _Sender = new EmailSender();
// 傳送按鈕
private void btnSend_Click(object sender, EventArgs e)
{
if (!_Sender.IsOver())
{
Log("之前的任務尚未完成,請等待完成!");
return;
}
// 1. 提取郵件列表並格式化顯示
string mails = txtEmailList.Text.Trim();
var list = ParseEmailList(mails);
// 2. 格式化顯示一下
txtEmailList.Clear();
foreach (var mail in list)
{
txtEmailList.AppendText(mail + "\r\n");
}
// 3. 發起任務
var b = _Sender.Send(list, txtTitle.Text.Trim(), txtContent.Text, Log);
Log(b ? "發起成功" : "發起失敗");
}
6 執行結果
6.1 配置資訊錯誤時
6.2 配置資訊正確時
7 專案原始碼
https://github.com/triplestudio/SimpleEmailSender
歡迎訪問