.net 邮件批量发送功能源码

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.net 邮件批量发送功能源码相关的知识,希望对你有一定的参考价值。

#define debug
 
using System;
using System.Text;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
 
 
namespace Mail_Test.Mail
{
    /// <summary>
    /// 非线程安全类
    /// 使用注意事项:
    /// 1、该类无需(也不能)在外部包裹多线程,因为内部有提供“异步发送”方法,内、外都使用多线程会导致线程池对可用资源的误判,从而创建过多阻塞线程。
    /// 2、MailHelper类的 m_autoDisposeSmtp 属性的使用,具体见此字段注释。
    /// 3、启用 UTF-8 字符编码
    /// </summary>
    public class MailHelper
    {
 
        #region 构造函数
 
        /// <summary>
        /// 构建 MailHelper 实例
        /// </summary>
        /// <param name="isAsync">是否启用异步邮件发送,默认为同步发送</param>
        public MailHelper(bool isAsync = false)
        {
            m_IsAsync = isAsync;
        }
 
        /// <summary>
        /// 构建 MailHelper 实例
        /// </summary>
        /// <param name="mSmtpClient">SmtpClient实例</param>
        /// <param name="autoReleaseSmtp">是否自动释放SmtpClient实例</param>
        /// <param name="isAsync">是否启用异步邮件发送</param>
        public MailHelper(SmtpClient mSmtpClient, bool autoReleaseSmtp, bool isAsync = false)
        {
            this.SetSmtpClient(mSmtpClient, autoReleaseSmtp);
            m_IsAsync = isAsync;
        }
 
        #endregion
 
        #region  计划邮件数量 和 已执行完成邮件数量
 
        // 记录和获取在大批量执行异步短信发送时已经处理了多少条记录
        // 1、根据此值手动或自动释放 SmtpClient .实际上没有需要根据此值进行手动释放,因为完全可以用自动释放替换此逻辑
        // 2、根据此值可以自己设置进度
        private long m_CompletedSendCount = 0;
        public long CompletedSendCount
        {
            get { return Interlocked.Read(ref m_CompletedSendCount); }
            private set { Interlocked.Exchange(ref m_CompletedSendCount, value); }
        }
 
        // 计划邮件数量
        private long m_PrepareSendCount = 0;
        public long PrepareSendCount
        {
            get { return Interlocked.Read(ref m_PrepareSendCount); }
            private set { Interlocked.Exchange(ref m_PrepareSendCount, value); }
        }
 
        #endregion
 
        #region 异步 发送邮件相关参数
 
        // 是否启用异步发送邮件
        private bool m_IsAsync = false;
 
        // 案例:因为异步发送邮件在SmtpClient处必须加锁保证一封一封的发送。
        // 这样阻塞了主线程。所以换用队列的方式以无阻塞的方式进行异步发送大批量邮件
 
        // 发送任务可能很长,所以使用 Thread 而不是用ThreadPool。(避免长时间暂居线程池线程),并且SmtpClient只支持一次一封邮件发送
        private Thread m_SendMailThread = null;
 
        private AutoResetEvent m_AutoResetEvent = null;
        private AutoResetEvent AutoResetEvent
        {
            get
            {
                if (m_AutoResetEvent == null)
                    m_AutoResetEvent = new AutoResetEvent(true);
                return m_AutoResetEvent;
            }
        }
 
        // 待发送队列缓存数量。单独开个计数是为了提高获取此计数的效率
        private int m_messageQueueCount = 0;
        // 因为 MessageQueue 可能在 m_SendMailThread 线程中进行出队操作,所以使用并发队列ConcurrentQueue.
        // 队列中的数据只能通过取消异步发送进行清空,或则就会每一元素都执行发送邮件
        private ConcurrentQueue<MailUserState> m_MessageQueue = null;
        private ConcurrentQueue<MailUserState> MessageQueue
        {
            get
            {
                if (m_MessageQueue == null)
                    m_MessageQueue = new ConcurrentQueue<MailUserState>();
                return m_MessageQueue;
            }
        }
 
        /// <summary>
        /// 在执行异步发送时传递的对象,用于传递给异步发生完成时调用的方法 OnSendCompleted 。
        /// </summary>
        public object AsycUserState { get; set; }
 
        #endregion
 
        #region 内部字段、属性
 
        private SmtpClient m_SmtpClient = null;
 
        /// <summary>
        /// 默认为false。设置在 MailHelper 类内部,发送完邮件后是否自动释放 SmtpClient 实例
        /// Smtp不管是在 MailHelper 内部还是在外部都必须进行主动释放,
        /// 因为:SmtpClient 没有提供 Finalize() 终结器,所以GC不会进行回收,只能使用完后主动进行释放,否则会发生内存泄露问题。
        ///
        /// 何时将 autoReleaseSmtp 设置为false,就是SmtpClient需要重复使用的情况,即需要使用“相同MailHelper”向“相同Smtp服务器”发送大批量的邮件时。
        /// </summary>
        private bool m_autoDisposeSmtp = false;
 
        /// <summary>
        /// 设置此电子邮件的收件人的地址集合。
        /// </summary>
        Dictionary<string, string> m_DicTo = null;
        Dictionary<string, string> DicTo
        {
            get
            {
                if (m_DicTo == null)
                    m_DicTo = new Dictionary<string, string>();
                return m_DicTo;
            }
        }
        /// <summary>
        /// 设置此电子邮件的抄送 (CC) 收件人的地址集合。
        /// </summary>
        Dictionary<string, string> m_DicCC = null;
        Dictionary<string, string> DicCC
        {
            get
            {
                if (m_DicCC == null)
                    m_DicCC = new Dictionary<string, string>();
                return m_DicCC;
            }
        }
        /// <summary>
        /// 设置此电子邮件的密件抄送 (BCC) 收件人的地址集合。
        /// </summary>
        Dictionary<string, string> m_DicBcc = null;
        Dictionary<string, string> DicBcc
        {
            get
            {
                if (m_DicBcc == null)
                    m_DicBcc = new Dictionary<string, string>();
                return m_DicBcc;
            }
        }
        // 附件集合
        Collection<Attachment> m_Attachments;
        Collection<Attachment> Attachments
        {
            get
            {
                if (m_Attachments == null)
                    m_Attachments = new Collection<Attachment>();
                return m_Attachments;
            }
        }
        // 指定一个电子邮件不同格式显示的副本。
        Collection<AlternateView> m_AlternateViews;
        Collection<AlternateView> AlternateViews
        {
            get
            {
                if (m_AlternateViews == null)
                    m_AlternateViews = new Collection<AlternateView>();
                return m_AlternateViews;
            }
        }
 
        #endregion
 
        #region 公开属性
 
        /// <summary>
        /// 设置此电子邮件的发信人地址。
        /// </summary>
        public string From { get; set; }
        /// <summary>
        /// 设置此电子邮件的发信人地址。
        /// </summary>
        public string FromDisplayName { get; set; }
 
        /// <summary>
        /// 设置此电子邮件的主题。
        /// </summary>
        public string Subject { get; set; }
        /// <summary>
        /// 设置邮件正文。
        /// </summary>
        public string Body { get; set; }
 
        /// <summary>
        /// 设置邮件正文是否为 html 格式的值。
        /// </summary>
        public bool IsBodyHtml { get; set; }
 
        private int priority = 0;
        /// <summary>
        /// 设置此电子邮件的优先级  0-Normal   1-Low   2-High
        /// 默认Normal。
        /// </summary>
        public int Priority
        {
            get { return this.priority; }
            set
            {
                if (value < 0 || value > 2)
                    priority = 0;
                else
                    priority = value;
            }
        }
 
        #endregion
 
        /// <summary>
        /// 重置 MailHelper 实例信息
        /// 不释放 SmtpClient 实例和相关的AutoReleaseSimple字段,因为存在异步发送。。这两个字段由SetSmtpClient方法设置
        /// </summary>
        public void Reset()
        {
            From = String.Empty;
            FromDisplayName = String.Empty;
            if (m_DicTo != null)
                m_DicTo.Clear();
            if (m_DicCC != null)
                m_DicCC.Clear();
            if (m_DicBcc != null)
                m_DicBcc.Clear();
            if (m_Attachments != null)
                m_Attachments.Clear();
            if (m_AlternateViews != null)
                m_AlternateViews.Clear();
 
            Subject = String.Empty;
            Body = String.Empty;
            IsBodyHtml = false;
            priority = 0;
 
            AsycUserState = null;
 
            // 1、不重置SmtpClient。根据 m_autoDisposeSmtp 参数自动释放或由外部主动释放
            // 2、不重置:异步待发送队列及队列计数,AutoResetEvent实例,执行异步发送线程,是否启用异步发送标识
        }
 
        #region SmtpClient 相关方法
 
        /// <summary>
        /// 检查此 MailHelper 实例是否已经设置了 SmtpClient
        /// </summary>
        /// <returns>true代表已设置</returns>
        public bool ExistsSmtpClient()
        {
            return m_SmtpClient != null ? true : false;
        }
 
        /// <summary>
        /// 设置 SmtpClient 实例 和是否自动释放Smtp的唯一入口
        /// 1、将内部 计划数量 和 已完成数量 清零,重新统计以便自动释放SmtpClient
        /// 2、若要对SmtpClent设置SendCompleted事件,请在调用此方法前进行设置
        /// </summary>
        /// <param name="mSmtpClient"> SmtpClient 实例</param>
        /// <param name="autoReleaseSmtp">设置在 MailHelper 类内部,发送完邮件后是否自动释放 SmtpClient 实例</param>
        public void SetSmtpClient(SmtpClient mSmtpClient, bool autoReleaseSmtp)
        {
#if DEBUG
            Debug.WriteLine("设置SmtpClient,自动释放为"   (autoReleaseSmtp ? "TRUE" : "FALSE"));
#endif
            m_SmtpClient = mSmtpClient;
            m_autoDisposeSmtp = autoReleaseSmtp;
 
            // 将内部 计划数量 和 已完成数量 清零,重新统计以便自动释放SmtpClient  (MailHelper实例唯一的清零地方)
            m_PrepareSendCount = 0;
            m_CompletedSendCount = 0;
 
            if (m_IsAsync && autoReleaseSmtp)
            {
                // 注册内部释放回调事件.释放对象---该事件不进行取消注册,只在释放SmtpClient时,一起释放   (所以SmtpClient与MailHelper绑定后,就不要再单独使用了)
                以上是关于.net 邮件批量发送功能源码的主要内容,如果未能解决你的问题,请参考以下文章

Python 实现一键批量发送电子邮件

Python邮件发送源码

Java实现发送邮件功能

java如何实现批量发送邮件

用python实现自动发邮件的功能

Domino自动批量注册用户邮件提醒