• 基于Lumisoft.NET实现的邮件发送功能


    在前面的一些文章中,有介绍过DotNet内置SMTP类的邮件发送功能,附件、嵌入图片的模式都有介绍,本文继续介绍Lumisoft.NET这个非常优秀的开源组件,用该组件来设计开发邮件工具,将变得更加方便,功能更加强大。网上很多文章基本介绍如何使用该组件来收取邮件较多,较少介绍使用该组件做邮件发送功能的。本文主要探寻使用该组件实现邮件的发送功能,邮件发送有两种方式,一种是不用发件人即可发送邮件,一种是使用发件人账户密码和SMTP服务器来实现邮件发送的,本文分别对这两种方式进行介绍。

    组件下载地址:http://www.lumisoft.ee/lswww/download/downloads/ 

    组件论坛地址:http://www.lumisoft.ee/Forum/default.aspx?g=forum 

    秉承一贯的做法,先贴出相关的实现图形,感官认识下,在进入详细的介绍说明,以求达到最好的理解深度。

    1、 首先是发件人的设置,可以从文本文件的导出,以及新建等操作,以方便用户操作。 

     

    2、 内容也支持导入导出,并且保存到数据库,方便进行记录及操作等,另外可以对内容进行随机混淆,混淆的内容在HTML邮件中式隐藏的,方便糊弄一下服务器的识别。

     
    3、 邮件发送可以选择两种方式,下面将分别介绍这两种方式的实现,一种采用该控件封装非常好的邮件直投技术,不需要SMTP账号发送的;一种是普通的SMTP发送方式。当然我们还可以设置更多的参数,例如邮件尾部信息、HTML内容提示、 以及一些发送期间自动拨号的设置操作等。


    4、 邮件直投技术,通过模拟账户来附加用户的邮件地址(或者可以成为伪装)。其中我填写了一些常用的SMTP服务器的域名,方便在其中构造合乎要求的邮件格式,还可以设置邮件回执通知,如下图所示。 

     

    5、 如果是采用普通发送方式,那么就需要制定用户的账号密码等信息,发送的时候,自动从启动获取发件人信息进行批量发送操作。


    6、 最后体验一下少量邮件的发送效果,发送采用多线程发送,多线程采用比较有名的SmartThreadPool组件,并且发送过程总详细记录其中的日志,供参考。

    介绍完毕相关的功能效果图,下面我们来分析下主要功能实现的代码:

     代码

            private TimerHelper timer = null;

            
    private void btnSend_Click(object sender, EventArgs e)
            {
                
    //重置计数变量
                failedItems = 0;
                successItems 
    = 0;

                workItemsCompleted 
    = 0;
                workItemsGenerated 
    = 0;

                Portal.gc.FailedCount 
    = 0;//重置失败次数

                STPStartInfo stpStartInfo 
    = new STPStartInfo();
                stpStartInfo.IdleTimeout 
    = 10;
                stpStartInfo.MaxWorkerThreads 
    = 100;
                stpStartInfo.MinWorkerThreads 
    = 0;
                
    //stpStartInfo.StartSuspended = true;
                _smartThreadPool = new SmartThreadPool(stpStartInfo);
                _workItemsGroup 
    = _smartThreadPool;

                workItemsProducerThread 
    = new Thread(new ThreadStart(this.WorkItemsProducer));
                workItemsProducerThread.IsBackground 
    = true;
                workItemsProducerThread.Start();

                RefreshStatusCount();

                
    int intervalRedial = SystemConfig.Default.IntervalRedial * 1000 * 60;
                
    if (intervalRedial > 0)
                {
                    
    if (timer != null)
                    {
                        timer.Stop();
                        timer.Dispose();
                    }
                    timer 
    = new TimerHelper(intervalRedial,false);
                    timer.Execute 
    += new TimerHelper.TimerExecution(timer_Execute);
                    timer.Start();
                }
            }

            
    private static object locker = new object();
            
    private void timer_Execute()
            {
                
    if (Monitor.TryEnter(locker))
                {
                    
    string message = string.Format("在时间 {0} 时刻执行了一次重拨号操作!", DateTime.Now);
                    ShowSendStatus(message);

                    
    string RasName = SystemConfig.Default.RasName;
                    
    if (!string.IsNullOrEmpty(RasName))
                    {
                        message 
    = string.Format("正在准备重新拨号({0})", RasName);
                        ShowSendStatus(message);

                        Portal.gc.ReConnect(RasName);
                        Portal.gc.FailedCount 
    = 0;//重新归零
                    }

                    Monitor.Exit(locker);
                }
                
    else
                {
                    Monitor.Enter(locker);
                    Monitor.Exit(locker);
                }
            }

     上面是主要的任务生成操作以及相关的拨号操作,其中任务详细的生成代码如下所示。

             private void WorkItemsProducer()

            {
                CallCtrlWithThreadSafetyEx.SetText(
    this.txtSendDetail, "");
                
                EnableControl(
    falsetruetrue);
                
    string message = string.Format("任务开始");
                RecordMessage(message);

                
    #region 生成任务

                IWorkItemsGroup workItemsGroup 
    = _workItemsGroup;
                
    if (null == workItemsGroup)
                {
                    
    return;
                }

                List
    <string> addressList = GetAddressList();
                List
    <MyMailInfo> mailInfoList = GetMailInfo();
                
    for (int i = 0; i < addressList.Count; i++)
                {
                    
    try
                    {
                        SendJobInfo jobInfo 
    = new SendJobInfo();
                        jobInfo.domainList 
    = mailDomainList;
                        jobInfo.mailTo 
    = addressList[i];
                        jobInfo.mailInfo 
    = GetOneMail(mailInfoList, i);
                        jobInfo.ShowSendStatus 
    = ShowSendStatus;
                        jobInfo.currentDomain 
    = (i % mailDomainList.Count);//设置一个标志,默认那个账户开始发送
                        jobInfo.UseDirectSendType = SystemConfig.Default.EmailDirectSend;

                        
    //如果用户未指定发送账号,那么采用默认的显示名称
                        
    //如果为空,发送的时候,会自动采用邮件地址作为显示名称
                        if (string.IsNullOrEmpty(SystemConfig.Default.UserEmailFrom))
                        {
                            jobInfo.mailFromDisplay 
    = SystemConfig.Default.DefaultFromDisplayName;
                        }

                        workItemCallback 
    = new WorkItemCallback(this.DoWork);
                        workItemsGroup.QueueWorkItem(workItemCallback, jobInfo);
                        Thread.Sleep(
    100);
                    }
                    
    catch (ObjectDisposedException ex)
                    {
                        LogTextHelper.WriteLine(ex.ToString());
                        
    continue;
                    }

                    Interlocked.Increment(
    ref workItemsGenerated);
                }

                
    #endregion

                RefreshStatusCount();
                message 
    = string.Format("共有 {0} 个任务,还剩下 {1} 个",
                    workItemsGenerated, workItemsGenerated 
    - workItemsCompleted);
                CallCtrlWithThreadSafetyEx.SetText(
    this, message);
                RecordMessage(message);

                
    try
                {
                    
    //workItemsGroup.Start();
                    workItemsGroup.WaitForIdle();
                    _smartThreadPool.Shutdown();
                }
                
    catch (Exception ex)
                {
                    LogTextHelper.WriteLine(ex.ToString());
                }

                UpdateFinishStatus();
            }

    由于采用了多线程来处理,所以停止发送的时候,需要把相关的线程对象进行释放,如下代码所示。

     代码

            private void btnStop_Click(object sender, EventArgs e)
            {
                
    try
                {
                    _smartThreadPool.Shutdown();
                    _smartThreadPool.Dispose();
                    _smartThreadPool 
    = null;

                    
    if (timer != null)
                    {
                        timer.Stop();
                        timer.Dispose();
                    }
                }
                
    catch (Exception ex)
                {
                    LogTextHelper.WriteLine(ex.ToString());
                }

                UpdateFinishStatus();
            }

     其中具体的邮件发送功能封装在SendJobInfo中,通过判断不同的类型,进行不同的发送操作。

    其中最为关键的发送代码,就是如何利用LumiSoft.NET组件来构造相应的邮件对象,下面先先介绍下邮件直投的发送方式,由于该组件封装比较好,直投发送方式很简单:

    Mail_Message message = Create_PlainText_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);

    SMTP_Client.QuickSend(message);

     其中Create_PlainText_Html_Attachment_Image的封装函数详细内容如下所示:

    代码
           private Mail_Message Create_PlainText_Html_Attachment_Image(string mailTo, string mailFrom, string mailFromDisplay)
            {
                Mail_Message msg 
    = new Mail_Message();
                msg.MimeVersion 
    = "1.0";
                msg.MessageID 
    = MIME_Utils.CreateMessageID();
                msg.Date 
    = DateTime.Now;
                msg.From 
    = new Mail_t_MailboxList();
                msg.From.Add(
    new Mail_t_Mailbox(mailFromDisplay, mailFrom));
                msg.To 
    = new Mail_t_AddressList();
                msg.To.Add(
    new Mail_t_Mailbox(mailTo, mailTo));
                msg.Subject 
    = mailInfo.Title;

                
    //设置回执通知
                string notifyEmail = SystemConfig.Default.DispositionNotificationTo;
                
    if (!string.IsNullOrEmpty(notifyEmail) && ValidateUtil.IsEmail(notifyEmail))
                {
                    msg.DispositionNotificationTo 
    = new Mail_t_Mailbox(notifyEmail, notifyEmail);
                }

                
    #region MyRegion
                
    //--- multipart/mixed -----------------------------------
                MIME_h_ContentType contentType_multipartMixed = new MIME_h_ContentType(MIME_MediaTypes.Multipart.mixed);
                contentType_multipartMixed.Param_Boundary 
    = Guid.NewGuid().ToString().Replace('-''.');
                MIME_b_MultipartMixed multipartMixed 
    = new MIME_b_MultipartMixed(contentType_multipartMixed);
                msg.Body 
    = multipartMixed;

                
    //--- multipart/alternative -----------------------------
                MIME_Entity entity_multipartAlternative = new MIME_Entity();
                MIME_h_ContentType contentType_multipartAlternative 
    = new MIME_h_ContentType(MIME_MediaTypes.Multipart.alternative);
                contentType_multipartAlternative.Param_Boundary 
    = Guid.NewGuid().ToString().Replace('-''.');
                MIME_b_MultipartAlternative multipartAlternative 
    = new MIME_b_MultipartAlternative(contentType_multipartAlternative);
                entity_multipartAlternative.Body 
    = multipartAlternative;
                multipartMixed.BodyParts.Add(entity_multipartAlternative);

                
    //--- text/plain ----------------------------------------
                MIME_Entity entity_text_plain = new MIME_Entity();
                MIME_b_Text text_plain 
    = new MIME_b_Text(MIME_MediaTypes.Text.plain);
                entity_text_plain.Body 
    = text_plain;
                
                
    //普通文本邮件内容,如果对方的收件客户端不支持HTML,这是必需的
                string plainTextBody = "如果你邮件客户端不支持HTML格式,或者你切换到“普通文本”视图,将看到此内容";
                
    if (!string.IsNullOrEmpty(SystemConfig.Default.PlaintTextTips))
                {
                    plainTextBody 
    = SystemConfig.Default.PlaintTextTips;
                }

                text_plain.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, plainTextBody);
                multipartAlternative.BodyParts.Add(entity_text_plain);

                
    //--- text/html -----------------------------------------
                string htmlText = mailInfo.Content;//"<html>这是一份测试邮件,<img src=\"cid:test.jpg\">来自<font color=red><b>LumiSoft.Net</b></font></html>";
                MIME_Entity entity_text_html = new MIME_Entity();
                MIME_b_Text text_html 
    = new MIME_b_Text(MIME_MediaTypes.Text.html);
                entity_text_html.Body 
    = text_html;
                text_html.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, htmlText);
                multipartAlternative.BodyParts.Add(entity_text_html);

                
    //--- application/octet-stream -------------------------
                foreach (string attach in mailInfo.Attachments)
                {
                    multipartMixed.BodyParts.Add(Mail_Message.CreateAttachment(attach));
                }

                
    foreach (string imageFile in mailInfo.EmbedImages)
                {
                    MIME_Entity entity_image 
    = new MIME_Entity();
                    entity_image.ContentDisposition 
    = new MIME_h_ContentDisposition(MIME_DispositionTypes.Inline);
                    
    string fileName = DirectoryUtil.GetFileName(imageFile, true);
                    entity_image.ContentID 
    = BytesTools.BytesToHex(Encoding.Default.GetBytes(fileName));                
                    MIME_b_Image body_image 
    = new MIME_b_Image(MIME_MediaTypes.Image.jpeg);
                    entity_image.Body 
    = body_image;
                    body_image.SetDataFromFile(imageFile, MIME_TransferEncodings.Base64);
                    multipartMixed.BodyParts.Add(entity_image);
                }

                
    #endregion

                
    return msg;
            }

    如果使用普通的账号方式发送SMTP邮件,主要代码如下所示,其中可以看出是利用了命令方式一步步和服务器进行交互的。

     using (SMTP_Client client = new SMTP_Client())
                        {
                            
    int port = domainInfo.Ssl ? WellKnownPorts.SMTP_SSL : WellKnownPorts.SMTP;
                            
    if (domainInfo.Port > 0)
                            {
                                port 
    = domainInfo.Port;
                            }

                            client.Connect(domainInfo.SmtpServer, port, domainInfo.Ssl);
                            client.Authenticate(domainInfo.Username, domainInfo.Password);
                            
    //string text = client.GreetingText;
                            client.MailFrom(mailFrom, -1);
                            client.RcptTo(mailTo);

                            MemoryStream stream 
    = Create_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);
                            client.SendMessage(stream);
                            client.Disconnect(); 
                        }

    其中构造邮件内容的代码和刚才的部分类似,详细代码如下所示。

            private MemoryStream Create_Html_Attachment_Image(string mailTo, string mailFrom, string mailFromDisplay)
            {
                Mime m 
    = new Mime();
                MimeEntity mainEntity 
    = m.MainEntity;

                mainEntity.From 
    = new AddressList();
                mainEntity.From.Add(
    new MailboxAddress(mailFromDisplay, mailFrom));

                mainEntity.To 
    = new AddressList();
                mainEntity.To.Add(
    new MailboxAddress(mailTo, mailTo));
                mainEntity.Subject 
    = mailInfo.Title;
                mainEntity.ContentType 
    = MediaType_enum.Multipart_mixed;

                MimeEntity textEntity 
    = mainEntity.ChildEntities.Add();
                textEntity.ContentType 
    = MediaType_enum.Text_html;
                textEntity.ContentTransferEncoding 
    = ContentTransferEncoding_enum.QuotedPrintable;
                textEntity.DataText 
    = mailInfo.Content;
    .........................         

            MemoryStream msg 
    = new MemoryStream();
                m.ToStream(msg);
                msg.Position 
    = 0;

                
    return msg;
            }

    利用Lumisoft.NET这个组件,可以实现很多相关的邮件操作,这里介于兴趣及篇幅原因,主要介绍邮件发送的功能模块,其中贴出的代码,一个是为了和感兴趣的朋友相互交流,一个也是为了自己今后做一个借鉴,并不鼓励大家用此软件或者代码来大批量发送垃圾邮件。 

    主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
    专注于Winform开发框架/混合式开发框架Web开发框架Bootstrap开发框架微信门户开发框架的研究及应用
      转载请注明出处:
    撰写人:伍华聪  http://www.iqidi.com 
        
  • 相关阅读:
    第一部分 类的初始
    第一部分 数据内存存储、常预定义值类型、用户自定义值类型、预定义引用类型
    第一部分 变量、数据类型
    第一部分 程序结分析、控制台输入输出
    第一部分 代码组织概念,集成开发环境(IDE)
    第一部分 记事本搞定第一个C#程序和编译过程剖析
    jQuery函数
    leaflet入门(五)API翻译(下)
    leaflet入门(四)API翻译(上)
    禁止浏览器缓存js
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/1913154.html
Copyright © 2020-2023  润新知