当前位置: 移动技术网 > IT编程>开发语言>.net > 深入Lumisoft.NET实现邮件发送功能的方法详解

深入Lumisoft.NET实现邮件发送功能的方法详解

2017年12月12日  | 移动技术网IT编程  | 我要评论

猪之旅,婆婆来了大结局,开江县

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网