asp.net发送邮件及自动发邮件的方法

发布时间:2020-10-14编辑:脚本学堂
asp.net发邮件的二种方法,重点介绍asp.net中自动发邮件功能的实现方法,自定义邮件发送类的实现代码,发送邮件通知到指定的EMAIL地址,asp.net发送邮件内容嵌入图片的方法。

一、asp.net发送邮件

需求:
在网站中加入发送邮件的功能,例如一个网上投稿系统,当稿件被采用时发送邮件通知作者。

以下重点介绍如何实现自动发送邮件?
 
1、实现发送邮件功能

首先,在.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");

2、asp.net发送邮件功能的实现过程

上面创建好了一个负责发送邮件的类,接下来的问题是应该在什么时候调用这个类。发送电子邮件需要进行网络通信,耗时比较多,而且SmtpClient的Send方法是会阻塞调用线程的,一旦调用了该方法,就要等到邮件发送完毕或出错才能结束方法调用,所以不能将对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,定时执行某个方法,在这个方法中进行条件检查并触发事件即可。

二、asp.net中发送邮件内容如何嵌入图片
.net中发送邮件内容嵌入图片

例1:
邮件内容调用图片格式为:<img src="cid:Email001">
 
发送邮件的服务端代码为:
SmtpClient 发送邮件的对象 //代码省略
 
代码:
 

复制代码 代码示例:
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);
 

 
例2:
 

复制代码 代码示例:
SmtpClient smtp = new SmtpClient();
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.Host = "smtp.163.com";
smtp.Credentials = new NetworkCredential("x", "**");
 
MailMessage mm = new MailMessage();
mm.From = new MailAddress("x@163.com", "测试");
mm.To.Add("renzhijie1990@vip.qq.com");
 
mm.Subject = "发送带图片邮件";
 
string plainTextBody = "如果你邮件客户端不支持HTML格式,或者你切换到“普通文本”视图,将看到此内容";
mm.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(plainTextBody, null, "text/plain"));
 
////HTML格式邮件的内容
string htmlBodyContent = "如果你的看到<b>这个</b>, 说明你是在以 <span style="color:red">HTML</span> 格式查看邮件<br><br>";
htmlBodyContent += "<a href="http://www.jb200.com//%22%3x</a> <img src="cid:weblogo">";   //注意此处嵌入的图片资源
AlternateView htmlBody = AlternateView.CreateAlternateViewFromString(htmlBodyContent, null, "text/html");
 
LinkedResource lrImage = new LinkedResource(@"d:1.jpg", "image/gif");
lrImage.ContentId = "weblogo"; //此处的ContentId 对应 htmlBodyContent 内容中的 cid: ,如果设置不正确,请不会显示图片
htmlBody.LinkedResources.Add(lrImage);
 
mm.AlternateViews.Add(htmlBody);
 
////要求回执的标志
mm.Headers.Add("Disposition-Notification-To", "x@163.com");
 
////自定义邮件头
mm.Headers.Add("X-Website", "http://www.jb200.com/");
 
////针对 LOTUS DOMINO SERVER,插入回执头
mm.Headers.Add("ReturnReceipt", "1");
 
mm.Priority = MailPriority.Normal; //优先级
mm.ReplyTo = new MailAddress("x@163.com", "我自己");
 
////如果发送失败,SMTP 服务器将发送 失败邮件告诉我
mm.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
 
////异步发送完成时的处理事件
smtp.SendCompleted += new SendCompletedEventHandler(smtp_SendCompleted);
 
////开始异步发送
smtp.SendAsync(mm, null);