一个简易邮件群发软件设计与实现
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
在日志框中输出内容,为了能在线程中调用,使用了 invoke 方式执行。 基本描述:给定邮箱列表,标题与内容,以线程方式执行发送,给出执行统计与状态。 为此,这里专门定义一个发送器,在应用中,定义一个实例来发起任务。尽管只定义一个实例,但这里并不需要定义为设计模式中的单例模式,事实上,它是可以多实例运行。具体代码说话! 发送按钮执行的流程为:如果之前的任务尚未完成,则等待。否则,首先提取邮箱列表,并格式化显示,然后发起任务,观察日志即可。 /// <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 日志输出方法准备
/// <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 项目源代码