zoukankan      html  css  js  c++  java
  • [ASP.NET]自动发送邮件功能的实现

    发送邮件功能的实现

    有时我们需要在网站中加入发送邮件的功能,例如一个网上投稿系统,当稿件被采用的时候发送邮件通知作者。下面就以这个功能为例说明如何实现自动发送邮件。

     

    实现发送邮件功能

    首先说一下在.Net下如何发送邮件。.Net已经为我们准备好了与发送邮件相关的类,只要直接调用即可,非常方便。下面是我自己写的一个邮件通知类:

     

    /// <summary>

    /// 邮件通知服务类。

    /// </summary>

    public class EmailNotificationService {

     

        /// <summary>

        /// 构造一个邮件通知服务类的实例。

        /// </summary>

        /// <param name="smtpService">SMTP服务器的IP地址</param>

        /// <param name="enableSSL">是否使用SSL连接SMTP服务器器</param>

        /// <param name="port">SMTP服务器端口</param>

        /// <param name="loginName">用于登录SMTP服务器的用户名</param>

        /// <param name="password">登录密码</param>

        public EmailNotificationService(

           string smtpService,

           bool enableSSL,

           int port,

           string loginName,

           string password) {

     

           this.m_smtpService = smtpService;

           this.m_loginName = loginName;

           this.m_password = password;

           this.m_enableSSL = enableSSL;

           this.m_port = port;

        }

     

        private readonly string m_smtpService;

        private readonly string m_loginName;

        private readonly string m_password;

        private readonly bool m_enableSSL;

        private readonly int m_port;

     

        /// <summary>

        /// 发送邮件通知到指定的EMAIL地址。

        /// </summary>

        /// <param name="senderName">显示在发件人一栏上的名称</param>

        /// <param name="address">目的EMAIL地址</param>

        /// <param name="title">邮件标题</param>

        /// <param name="content">邮件内容</param>

        public void SendTo(string senderName, string address, string title, string content) {

              

           MailMessage mail = new MailMessage();

           mail.To.Add(address);

           mail.From = new MailAddress(this.m_loginName, senderName, Encoding.UTF8);

           mail.Subject = title;

           mail.Body = content;

           mail.BodyEncoding = Encoding.UTF8;

           mail.IsBodyHtml = false;

           mail.Priority = MailPriority.Normal;

     

           SmtpClient smtp = new SmtpClient();

           smtp.Credentials = new NetworkCredential(this.m_loginName, this.m_password);

           smtp.Host = this.m_smtpService;

           smtp.EnableSsl = this.m_enableSSL;

           smtp.Port = this.m_port;

     

           smtp.Send(mail);

        }

    }

     

    在使用时,首先构造一个EmailNotificationService类,再调用SendTo方法即可。例如:

     

    EmailNotificationService mailNotificationService = new EmailNotificationService("smtp.gmail.com", true, 587, "LoginName@gmail.com", "LoginPassword");

     

    mailNotificationService.SendTo("SenderName", "TargetAddress@qq.com", "Title", "Content");

    发送邮件实现方案

    上面创建好了一个负责发送邮件的类,接下来的问题是应该在什么时候调用这个类。发送电子邮件需要进行网络通信,耗时比较多,而且SmtpClientSend方法是会阻塞调用线程的,一旦调用了该方法,就要等到邮件发送完毕或出错才能结束方法调用,所以不能将对EmailNotificationService的调用放在ASP.NET页面的代码中。如果这么做,客户端就要等待很长时间才能获得响应,用户体验是比较差的。

     

    SmtpClient还有一个SendAsync方法,该方法与Send方法的区别是,SendAsync是异步的,调用该方法之后会产生一个新的线程来负责发送邮件,之后调用线程立即返回,不会再等待邮件发送结束。那么我们是不是可以用SendAsync代替Send,并在页面代码中调用呢?答案是否定的,虽然客户端可以很快获得相应,但邮件根本没有发送出去。这是由ASP.NET页面生命周期的特性决定的,客户端向服务器的每一次请求,页面都会经历一个由产生到销毁的过程,当页面销毁的时候,负责发送邮件的线程还没有完成发送邮件的工作就被强制结束了。

     

    由于ASP.NET页面生命周期的特性,我们不能将调用代码放在页面的代码中。我们需要一个与页面无关的线程,一个在网站运行时始终存在的线程。我的方案是使用一个全局对象来管理一个发送邮件线程,同时维护一个待发送邮件链表。当全局对象创建的时候,链表中没有任何内容,发送邮件线程处于挂起状态。当某个页面中的处理需要发送电子邮件时,就将与发送邮件相关的信息添加到待发送邮件链表中。此时链表不为空,发送邮件线程开始工作,逐个取出链表中的邮件信息并发送,一直到链表为空,再次进入挂起状态。如此循环反复。

     

    实现发送邮件功能

    基本的构思已经确定好了,接下来就是写代码实现了。首先定义一个类来封装待发送邮件的相关信息,本文开头已经说过要以一个网上投稿系统作为例子,所以这里所用的信息与该应用有关。

     

    /// <summary>

    /// 封装发送邮件时所需信息的类。

    /// </summary>

    public class MailNotifyInfo  {

     

        /// <summary>

        /// 获取或设置稿件的标题。

        /// </summary>

        public string Title {

     

           get;

           set;

        }

     

        /// <summary>

        /// 获取或设置稿件的作者名称。

        /// </summary>

        public string Author {

     

           get;

           set;

        }

     

        /// <summary>

        /// 获取或设置作者的电子邮件地址。

        /// </summary>

        public string EmailAddress {

     

            get;

            set;

        }

     

        /// <summary>

        /// 获取或设置稿件的状态。

        /// </summary>

        public ArticleStatus ArticleStatus {

     

           get;

           set;

        }

    }

     

    然后是全局对象类的定义,我使用了单件模式来实现其全局性。

     

    /// <summary>

    /// 处理邮件发送功能的类。

    /// </summary>

    public class NotificationHandler {

     

        /// <summary>

        /// 该类的静态实例。

        /// </summary>

        private static readonly NotificationHandler g_instance = new NotificationHandler();

     

        /// <summary>

        /// 获取该类的唯一实例。

        /// </summary>

        public static NotificationHandler Instance {

     

           get {

     

               return g_instance;

           }

        }

     

        /// <summary>

        /// 默认构造方法。

        /// </summary>

        private NotificationHandler() {

     

            this.m_lockObject = new object();

     

            this.m_mailNotifyInfos = new LinkedList<MailNotifyInfo>();

            this.m_threadEvent = new ManualResetEvent(false);

     

            this.m_workThread = new Thread(this.ThreadStart);

            this.m_workThread.Start();

        }

     

        private readonly LinkedList<MailNotifyInfo> m_mailNotifyInfos;

        private readonly Thread m_workThread;

        private readonly ManualResetEvent m_threadEvent;

        private readonly Object m_lockObject;

     

        /// <summary>

        /// 添加待发送邮件的相关信息。

        /// </summary>

        public void AppendNotification(MailNotifyInfo mailNotifyInfo) {

     

    lock (this.m_lockObject) {

     

               this.m_mailNotifyInfos.AddLast(mailNotifyInfo);

     

               if (this.m_mailNotifyInfos.Count != 0) {

     

                  this.m_threadEvent.Set();

               }

           }

        }

     

        /// <summary>

        /// 发送邮件线程的执行方法。

        /// </summary>

        private void ThreadStart() {

     

           while (true) {

     

               this.m_threadEvent.WaitOne();

     

               MailNotifyInfo mailNotifyInfo = this.m_mailNotifyInfos.First.Value;

     

               EmailNotificationService mailNotificationService = new EmailNotificationService("smtp.gmail.com", true, 587, "LoginName@gmail.com", "LoginPassword");

     

               mailNotificationService.SendTo("稿件中心",

                                              mailNotifyInfo.EmailAddress,

                                              "稿件状态变更通知",

                                              String.Format("{0}你的稿件{1}状态已变更为{2}", mailNotifyInfo.Author, mailNotifyInfo.Title, mailNotifyInfo.ArticleStatus));

     

               lock (this.m_lockObject) {

     

                  this.m_mailNotifyInfos.Remove(mailNotifyInfo);

     

                  if (this.m_mailNotifyInfos.Count == 0) {

     

                      this.m_threadEvent.Reset();

                  }

               }

           }

    }

     

    该类比较简单,首先在构造函数中初始化成员变量,然后启动发送邮件线程,此时该线程是挂起的。

     

    当外部调用AppendNotification方法时,会在链表中添加一个MailNotifyInfo对象,然后唤醒发送邮件线程。由于在生产环境下可能会出现同时调用AppendNotification方法的情形,所以这里要进行同步。

     

    发送邮件线程唤醒后进入一个死循环,等待事件对象触发。当事件对象出发之后就开始发送邮件了。邮件发送完毕后从链表中删除已发送的邮件,然后检查链表是否为空,如果是则重置事件对象,重新进入挂起状态。同样地,在对链表进行操作时也要进行同步。

     

    至此,发送邮件的功能实现完毕。需要发送邮件的时候只要像这样调用即可:

     

    MailNotifyInfo mailNotifyInfo = new MailNotifyInfo();

    .....

    NotificationHandler.Instance.AppendNotification(mailNotifyInfo);

     

    这只是一个很粗陋的框架,而且还不完善。例如,这里假设网站是不间断运行的系统,没有考虑当网站关闭时发送邮件线程的处理。大家可以在这个基础上添砖加瓦,使其更加完善。另外,自动发送邮件也是常见的功能,例如定时检查某个条件,如果成立则发送邮件。要实现自动发送邮件的话,只要对本文的方案稍加修改即可:在NotificationHandler中添加一个Timer,定时执行某个方法,在这个方法中进行条件检查并触发事件即可。

  • 相关阅读:
    Bootstrap
    Asp.Net母版页的相关知识
    连接Access数据库的连接字符串
    登录界面更换验证码图片
    .NET中string与StringBuilder在字符串拼接功能上的比较
    Asp.Net(C#)使用oleDbConnection 连接Excel
    非常规方法实现添加合并列
    ASP.Net页面间变量值传递的方法
    eclipse配置class注释模板
    spring中web.xml指定配置文件
  • 原文地址:https://www.cnblogs.com/zplutor/p/1743074.html
Copyright © 2011-2022 走看看