【.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; }
    }
}
相关推荐
wanhengidc8 小时前
云手机 高振畅玩不踩坑
运维·服务器·安全·web安全·智能手机
有谁看见我的剑了?8 小时前
linux 添加硬盘后系统识别不到硬盘处理
linux·运维·服务器
John_ToDebug8 小时前
隐于无形,触手可及:Chrome 互动滚动条的六个设计密码
chrome·windows·ui
酿情师8 小时前
yihan:一款面向连续网页学习的智能侧边栏插件
学习·学习方法·工具·学习工具
思茂信息9 小时前
CST软件如何进行参数化扫描?
运维·开发语言·javascript·windows·ecmascript·软件工程·软件需求
瞎某某Blinder9 小时前
DFT学习记录[6]基于 HES06的能带计算+有效质量计算
python·学习·程序人生·数据挖掘·云计算·学习方法
金色光环10 小时前
FreeModbus释放底层的 TCP 监听端口
服务器·网络·tcp/ip
love在水一方10 小时前
VLN 入门学习计划 —— 基于 InternNav
学习
开发者联盟league10 小时前
在windows上安装和运行rocketmq
windows·rocketmq