Winform开发邮件客户端小工具
需求如下:
群发邮件,要监控到哪些人打开过这封邮件,同一个人打开多次,或者用不同的工具(PC的web端,客户端,移动端的wap,app)都只能算作一次
解决过程:
第一次是打算在邮件内容里加上cnzz统计代码来统计,但是邮箱在发送邮件的过程中直接把js干掉了,尴了个尬!
第二次是通过在邮件内容追加一个<img src="http:xxx.com" style="display:none;" />来监控,在服务器端查看访问者的Referer中的mailid来标识是否打开过该邮件,但是发现使用客户端软件(如 Foxmail或者app)打开后,并没有Referer,如果用户只用客户端软件看了一次邮件的话,那么你就监控不到是否打开,只能又是尴尬的结束了。
第三次是自己开发一个发送邮件的客户端,然后将mailid和user作为该封邮件被指定的邮箱打开过的唯一标识。其实就是在邮件内容里嵌入一个监控的脚本
<img src="http://email.ds.net/emailmonitor.aspx?mailid=xxx&user=xxx" width="1px" height="1px" />,只要邮件一旦加载就可以进行监控。
开发心得:
1、发送邮件:
在.NET里已经对发送邮件有一个封装好的类库,实现发送邮件功能就要在此基础上。下面是我的发送邮件的方法(因为我的邮件内容支持html和图片功能,所以不能直接使用 msg.Body = content;这种直接绑定内容的方式,普通的发送是可以使用这种的)
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="account">发件人账户</param>
/// <param name="pwd">发件人密码</param>
/// <param name="user">收件人账户</param>
/// <param name="subject">邮件主题</param>
/// <param name="attach">邮件附件</param>
/// <param name="htmlBody">邮件内容(html格式包含样式和图片)</param>
public static void SendEmail(string account, string pwd, string user, string subject, List<string> attach, AlternateView htmlBody)
{
MailMessage msg = new MailMessage();
msg.To.Add(user);
//msg.CC.Add("c@c.com"); // 抄送人地址
msg.From = new MailAddress(account, "", System.Text.Encoding.UTF8);
msg.Subject = subject;
msg.SubjectEncoding = System.Text.Encoding.UTF8;
// msg.Body = content; //邮件内容,不能发送图片的时候使用这种方式
msg.BodyEncoding = System.Text.Encoding.UTF8;
msg.IsBodyHtml = true;
msg.Priority = MailPriority.High;
msg.AlternateViews.Add(htmlBody); //此种方式不使用邮件正文的形式主要是为了发送图片
//添加所有附件
if (attach.Count > 0)
{
foreach (var item in attach)
{
msg.Attachments.Add(new Attachment(item));
}
}
SmtpClient client = new SmtpClient();
//////顺序很重要,这个只适合腾讯企业邮箱
client.Host = "smtp.exmail.qq.com";
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(account, pwd);
client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
client.Timeout = 10000;
client.Send(msg);
}
2、添加附件:
添加附件的时候要打开一个对话框,可以对文件进行单选或者多选,只需将文件名添加到一个list里面,然后将list里的文件名添加到附件属性里
if (attach.Count > 0)
{
foreach (var item in attach)
{
msg.Attachments.Add(new Attachment(item));
}
}
/// <summary> /// 添加附件 /// </summary> /// <param name="attach">附件名称集合</param> public static void AddAttach(List<string> attach) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "请选择上传的文件"; ofd.Filter = "所有文件|*"; //也可以指定某些格式允许作为附件进行上传 ofd.Multiselect = true; //是否多选 if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) //确定 { string[] filePath = ofd.FileNames; foreach (var item in filePath) { attach.Add(item); } } }
3、邮件内容嵌入图片:
邮件内容里嵌入图片一共有2中方式:第一种是以流的方式(将图片以Base64方式编码);第二种是将图片以类似于附件中嵌入外部资源的形式上传,然后在替换成显示的图片(linked方式)。
首先介绍一下流的方式:
查阅了一些资料得知邮件在网路上传输一般都要通过base 64编码,然后直接赋值给Src
<img src="data:image/png;base64, SkZJRgABAQEAZA…" />,那这个"乱码"肯定就是图片了!
Bitmap bmp = new Bitmap(本地文件名); MemoryStream ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); byte[] arr = new byte[ms.Length]; ms.Position = 0; ms.Read(arr, 0, (int)ms.Length); ms.Close(); String strbaser64 = Convert.ToBase64String(arr);
<img src="data:image/png;base64,strbaser64 " />
再介绍一下以外部资源的方式上传然后在显示成图片的方式:
保证邮件内容里的图片的src是这样的一种形式 <img src=\"cid:Email001\">然后给这张图片对应的外部资源的ContentId 属性和cid的值一致,然后在绑定该外部资源对应的真实的图片的路径及文件名就可以了
邮件内容调用图片格式为:<img src=\"cid:Email001\">
System.NET.Mail.MailMessage mailMessage = new System.Net.Mail.MailMessage(); mailMessage.From="发送者邮箱"; mailMessage.To.Add("收件人邮件列表"); mailMessage.CC.Add("抄送人邮件列表"); mailMessage.Subject = subject; AlternateView htmlBody = AlternateView.CreateAlternateViewFromString(content,null,"text/html"); LinkedResource lrImage = new LinkedResource("a.jpg","image/gif"); lrImage.ContentId = "Email001"; htmlBody.LinkedResources.Add(lrImage); mailMessage.AlternateViews.Add(htmlBody); SmtpClient.Send(mailMessage);
在本程序中要先将所有的上传的图片利用正则,将其路径替换成具有唯一标识的cid这种情况,然后再为每一个cid绑定真实的图片路径
/// <summary> /// 将正文(html)中的图片以linked(类似于以附件中嵌入外部资源的形式上传,然后在替换成显示的图片)方式显示图片 /// </summary> /// <param name="mc"></param> /// <param name="newContent"></param> /// <returns></returns> public static AlternateView GetImagesByLinked(MatchCollection mc,string newContent) { for (int j = 0; j < mc.Count; j++) { newContent = newContent.Replace(mc[j].Groups[1].Value, "cid:Email00" + j.ToString()); } AlternateView htmlBody = AlternateView.CreateAlternateViewFromString(newContent, null, "text/html"); for (int j = 0; j < mc.Count; j++) { LinkedResource lrImage = new LinkedResource(mc[j].Groups[1].Value, "image/jpg"); lrImage.ContentId = "Email00" + j.ToString(); htmlBody.LinkedResources.Add(lrImage); } return htmlBody; }
4、本系统中的核心发送邮件代码:
time = DateTime.Now.ToString("yyyyMMddHHmmss"); string content = htmlEditor1.BodyInnerHTML; if (string.IsNullOrEmpty(content)) { MessageBox.Show("内容不能为空!"); return; } Regex reg = new Regex(@"<IMG[\s\S]+?src=""(.+?)""[\s\S]*?>"); MatchCollection mc = reg.Matches(content); string newContent = ""; //发送邮件前,先将邮件记录到数据库里 url = string.Format("http://email.ds.net/EmailData.aspx?subject={0}&mailid={1}&content={2}&ope=1", System.Web.HttpUtility.UrlEncode(txtSubject.Text), time, System.Web.HttpUtility.UrlEncode(content)); try { if (wc.DownloadString(url) != "OK") { MessageBox.Show("邮件未发送,保存邮件记录到数据库失败!"); DS.Applications.General.Exception.ExceptionHandle.Debug("邮件未发送,保存邮件记录到数据库失败!"); return; } else { DS.Applications.General.Exception.ExceptionHandle.Debug("保存邮件记录到数据库成功!下面开始发送邮件"); } } catch (Exception ex) { DS.Applications.General.Exception.ExceptionHandle.DebugFormat("邮件未发送,保存邮件记录到数据库失败!错误信息是:{0}", ex.Message); //return; } //开始发送邮件 for (int i = 0; i < arrRec.Length; i++) { newContent = content + "<img src=\"" + "http://email.ds.net/emailmonitor.aspx?mailid=" + time + "&user=" + arrRec[i] + "\" width=\"1px\" height=\"1px\" />"; AlternateView htmlBody = EmailHelper.GetImagesByLinked(mc, newContent); try { EmailHelper.SendEmail(txtAccount.Text, txtPwd.Text, arrRec[i].ToString(), txtSubject.Text, attach, htmlBody); DS.Applications.General.Exception.ExceptionHandle.Debug(arrRec[i].ToString() + "邮件发送成功!"); } catch (Exception ex) { failList.Add(arrRec[i].ToString()); DS.Applications.General.Exception.ExceptionHandle.DebugFormat("{0}邮件发送失败!错误信息是:{1}", arrRec[i].ToString(), ex.Message); } }
浙公网安备 33010602011771号