【.Net 6.0--通用帮助类--EmailHelper】

前言

邮件帮助类(smtp协议),需要NuGet引用MailKit包,包含了同步发送邮件(SendEmail)、异步发送邮件( SendEmailAsync)方法,由于非企业邮箱每日有发送限额,故代码中支持轮询多个邮箱,当某个邮箱超限时,标记为不可用状态,当天不再使用,以确保邮件正常发送成功,同时支持多种邮箱类型,见下表。

下表为常用邮箱服务器的地址和端口(SMTP/POP)

邮箱类型 SMTP-服务器地址 SMTP-端口号 POP-服务器地址 POP-端口号 是否SSL
163邮箱 smtp.163.com 25 pop.163.com 110
126邮箱 smtp.126.com 25 pop.126.com 110
139邮箱 smtp.139.com 25 pop.139.com 110
QQ邮箱 smtp.qq.com 25 pop.qq.com 110
QQ企业邮箱 smtp.exmail.qq.com 587/465 pop.exmail.qq.com 995
Gmail邮箱 smtp.gmail.com 587 pop.gmail.com 995
Foxmail邮箱 smtp.foxmail.com 25 pop.foxmail.com 110
Sina邮箱 smtp.sina.com.cn 25 pop3.sina.com.cn 110
SinaVIP邮箱 smtp.vip.sina.com 25 pop3.vip.sina.com 110
Sohu邮箱 smtp.sohu.com 25 pop3.sohu.com 110
Yahoo邮箱 smtp.mail.yahoo.com.cn 587 pop.mail.yahoo.com.cn 995

代码中的email.json格式如下,请自行配置个人邮箱:

json 复制代码
[
	{
		"Host": "smtp.163.com",
		"EnableSsl": true,
		"Port": 465,
		"SendAddress": "xxxx@163.com",
		"LoginPassword": "邮箱登录密码",
		"Password": "smtp分配的密码,代码中使用此密码",
		"CanUse": true
	}
]

代码示例

C# 复制代码
using MailKit.Net.Smtp;
using MimeKit;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using VW.API.Common.Models;

namespace VW.API.Common.Utils
{
    /// <summary>
    /// EmailHelper 的摘要说明:邮件帮助类
    /// </summary>
    public class EmailHelper
    {
        private static readonly string _emailConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs/email.json");

        /// <summary>
        /// 创建Email
        /// </summary>
        /// <param name="email">邮箱地址</param>
        /// <param name="subject">邮件主题</param>
        /// <param name="message">邮件内容</param>
        /// <param name="attachments">附件</param>
        /// <returns>Email</returns>
        private static SmtpClient GetSmtpClient(string email, string subject, string message, List<string> attachments, EmailModel emailModel, out MimeMessage mimeMessage)
        {
            try
            {
                var client = new SmtpClient();

                client.MessageSent += (sender, args) =>
                {
                    LogHelper.Error(args.Message.ToString());
                };

                client.ServerCertificateValidationCallback = (sender, certificate, certChainType, errors) => true;
                client.Connect(emailModel.Host, emailModel.Port, emailModel.EnableSsl);
                client.Authenticate(emailModel.SendAddress, emailModel.Password);
                
                mimeMessage = new MimeMessage();
                mimeMessage.From.Add(new MailboxAddress(emailModel.SendAddress, emailModel.SendAddress));
                mimeMessage.To.Add(new MailboxAddress(email, email));
                mimeMessage.Subject = subject;
                var builder = new BodyBuilder();
                builder.HtmlBody = message;
                foreach (var attachment in attachments)
                {
                    builder.Attachments.Add(attachment);
                }
                mimeMessage.Body = builder.ToMessageBody();
                
                return client;
            }
            catch (Exception) { throw; }
        }

        /// <summary>
        /// 发送邮件(同步)
        /// </summary>
        /// <param name="email">邮箱地址</param>
        /// <param name="subject">邮件主题</param>
        /// <param name="message">邮件内容</param>
        /// <param name="attachments">附件</param>
        /// <returns>bool</returns>
        public static bool SendEmail(string email, string subject, string message, List<string> attachments = null)
        {
            bool success = false;

            string lastDayFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Configs/{DateTime.Now.AddDays(-1).ObjToDateSplitYMD()}.email.json");
            FileHelper.DeleteFile(lastDayFilePath);
            string todayFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Configs/{DateTime.Now.ObjToDateSplitYMD()}.email.json");
            if (!FileHelper.IsExistFile(todayFilePath))
                FileHelper.Copy(_emailConfigPath, todayFilePath, true);

            List<EmailModel> emailModels = JsonConvert.DeserializeObject<List<EmailModel>>(FileHelper.FileToString(todayFilePath));
            foreach (EmailModel emailModel in emailModels)
            {
                if (!emailModel.CanUse)//不能使用
                    continue;

                try
                {
                    var client = GetSmtpClient(email, subject, message, attachments, emailModel, out MimeMessage mimeMessage);
                    client.Send(mimeMessage);
                    client.Disconnect(true);
                    //client.Dispose();

                    success = true;
                    break;
                }
                catch (Exception ex)
                {
                    LogHelper.Error(JsonConvert.SerializeObject(emailModel));
                    LogHelper.Error(ex.ToString());
                    emailModel.CanUse = false;
                    success = false;
                }
            }

            FileHelper.WriteText(todayFilePath, JsonConvert.SerializeObject(emailModels));

            return success;
        }

        /// <summary>
        /// 发送邮件(异步)
        /// </summary>
        /// <param name="email">邮箱地址</param>
        /// <param name="subject">邮件主题</param>
        /// <param name="message">邮件内容</param>
        /// <param name="attachments">附件</param>
        /// <returns>bool</returns>
        public static async Task<bool> SendEmailAsync(string email, string subject, string message, List<string> attachments)
        {
            bool success = false;

            string lastDayFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Configs/{DateTime.Now.AddDays(-1).ObjToDateSplitYMD()}.email.json");
            FileHelper.DeleteFile(lastDayFilePath);
            string todayFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Configs/{DateTime.Now.ObjToDateSplitYMD()}.email.json");
            if (!FileHelper.IsExistFile(todayFilePath))
                FileHelper.Copy(_emailConfigPath, todayFilePath, true);

            List<EmailModel> emailModels = JsonConvert.DeserializeObject<List<EmailModel>>(FileHelper.FileToString(todayFilePath));
            foreach (EmailModel emailModel in emailModels)
            {
                if (!emailModel.CanUse)//不能使用
                    continue;

                try
                {
                    var client = GetSmtpClient(email, subject, message, attachments, emailModel, out MimeMessage mimeMessage);
                    await client.SendAsync(mimeMessage);
                    client.Disconnect(true);
                    //client.Dispose();

                    success = true;
                    break;
                }
                catch (Exception ex)
                {
                    LogHelper.Error(JsonConvert.SerializeObject(emailModel));
                    LogHelper.Error(ex.ToString());
                    emailModel.CanUse = false;
                    success = false;
                }
            }

            FileHelper.WriteText(todayFilePath, JsonConvert.SerializeObject(emailModels));

            return success;
        }
    }
}
C# 复制代码
namespace VW.API.Common.Models
{
    /// <summary>
    /// EmailModel
    /// </summary>
    public class EmailModel
    {
        /// <summary>
        /// 主机地址
        /// </summary>
        public string Host { set; get; }
        /// <summary>
        /// 是否使用SSL
        /// </summary>
        public bool EnableSsl { set; get; }
        /// <summary>
        /// 端口号
        /// </summary>
        public int Port { set; get; }
        /// <summary>
        /// 邮件地址
        /// </summary>
        public string SendAddress { set; get; }
        /// <summary>
        /// 登录密码
        /// </summary>
        public string LoginPassword { set; get; }
        /// <summary>
        /// 邮件STMP授权码
        /// </summary>
        public string Password { set; get; }
        /// <summary>
        /// 是否可用
        /// </summary>
        public bool CanUse { set; get; }
    }
}
相关推荐
-一杯为品-8 分钟前
【51单片机】程序实验5&6.独立按键-矩阵按键
c语言·笔记·学习·51单片机·硬件工程
zhy81030229 分钟前
.net6 使用 FreeSpire.XLS 实现 excel 转 pdf - docker 部署
pdf·.net·excel
戎梓漩35 分钟前
windows下安装curl,并集成到visual studio
ide·windows·visual studio
初九之潜龙勿用41 分钟前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
我言秋日胜春朝★1 小时前
【Linux】进程地址空间
linux·运维·服务器
风尚云网1 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
C-cat.1 小时前
Linux|环境变量
linux·运维·服务器
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端