前言
邮件帮助类(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格式如下,请自行配置个人邮箱:
[{"Host": "smtp.163.com","EnableSsl": true,"Port": 465,"SendAddress": "xxxx@163.com","LoginPassword": "邮箱登录密码","Password": "smtp分配的密码,代码中使用此密码","CanUse": true}
]
代码示例
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;}}
}
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; }}
}