【.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; }
    }
}
相关推荐
Hello_Embed20 小时前
STM32HAL 快速入门(二十):UART 中断改进 —— 环形缓冲区解决数据丢失
笔记·stm32·单片机·学习·嵌入式软件
咸甜适中20 小时前
rust语言 (1.88) 学习笔记:客户端和服务器端同在一个项目中
笔记·学习·rust
Magnetic_h21 小时前
【iOS】设计模式复习
笔记·学习·ios·设计模式·objective-c·cocoa
研华嵌入式21 小时前
如何在高通跃龙QCS6490 Arm架构上使用Windows 11 IoT企业版?
arm开发·windows·嵌入式硬件
Tisfy21 小时前
服务器 - 从一台服务器切换至另一台服务器(损失数十条访客记录)
运维·服务器
研梦非凡1 天前
ICCV 2025|从粗到细:用于高效3D高斯溅射的可学习离散小波变换
人工智能·深度学习·学习·3d
猎板PCB黄浩1 天前
从废料到碳减排:猎板 PCB 埋容埋阻的绿色制造革命,如何实现环保与性能双赢
java·服务器·制造
limengshi1383921 天前
机器学习面试:请介绍几种常用的学习率衰减方式
人工智能·学习·机器学习
知识分享小能手1 天前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
追逐时光者1 天前
C#/.NET/.NET Core技术前沿周刊 | 第 54 期(2025年9.8-9.14)
后端·.net