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

本文转自博客园伍华聪的博客,原文链接:基于Lumisoft.NET实现的邮件发送功能,如需转载请自行联系原博主。



目录
相关文章
|
1月前
|
存储 文字识别 C#
.NET开源免费、功能强大的 Windows 截图录屏神器
今天大姚给大家分享一款.NET开源免费(基于GPL3.0开源协议)、功能强大、简洁灵活的 Windows 截图、录屏、Gif动图制作神器:ShareX。
|
11天前
|
定位技术 API C#
.NET开源、功能强大、跨平台的图表库
.NET开源、功能强大、跨平台的图表库
|
12天前
|
人工智能 开发框架 .NET
【专栏】解密.NET 技术的强大功能
【4月更文挑战第29天】这篇文章探讨了.NET技术的关键特性,如多语言支持、统一运行时、丰富的类库和跨平台能力,以及其在企业应用、Web开发、移动应用和游戏开发中的应用。文章指出,.NET正通过性能优化、容器化支持、AI整合和开源策略适应未来软件开发需求,强调了开发者应掌握.NET技术并参与社区,共同推动技术发展。
|
2月前
|
Windows
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
230 0
|
3月前
|
C# Windows
.NET开源的一个小而快并且功能强大的 Windows 动态桌面软件
.NET开源的一个小而快并且功能强大的 Windows 动态桌面软件
|
8月前
|
Apache
基于commons-net实现ftp创建文件夹、上传、下载功能.
基于commons-net实现ftp创建文件夹、上传、下载功能.
108 0
|
5月前
|
数据采集 消息中间件 安全
.NET使用分布式网络爬虫框架DotnetSpider快速开发爬虫功能
.NET使用分布式网络爬虫框架DotnetSpider快速开发爬虫功能
|
6月前
|
开发框架 前端开发 .NET
用ajax和asp.net实现智能搜索功能
用ajax和asp.net实现智能搜索功能
46 0
|
10月前
|
SQL 安全 前端开发
.NET开源免费功能最全的商城项目
.NET开源免费功能最全的商城项目
|
10月前
|
开发框架 前端开发 JavaScript
WPF+ASP.NET SignalR实现简易在线聊天功能
WPF+ASP.NET SignalR实现简易在线聊天功能
136 0