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);
                }                
            }

 


posted on 2017-01-06 15:52  奔游浪子  阅读(484)  评论(0)    收藏  举报

导航