前言
邮件帮助类(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; }
}
}