328 lines
13 KiB
C#
328 lines
13 KiB
C#
using MailKit.Net.Smtp;
|
||
using MailKit.Security;
|
||
using MimeKit;
|
||
using Newtonsoft.Json;
|
||
using System.IO.Compression;
|
||
|
||
namespace YLErp.MailKit
|
||
{
|
||
public static class MailSender
|
||
{
|
||
/// <summary>
|
||
/// 发送邮件
|
||
/// </summary>
|
||
public static void Send(SmtpConfig smtpConfig, MailSendingOption option, Action<string, string> log = null)
|
||
{
|
||
|
||
if (smtpConfig == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(smtpConfig));
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(smtpConfig.Server))
|
||
{
|
||
throw new ArgumentException("未设置SMTP Server");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(smtpConfig.UserName))
|
||
{
|
||
throw new ArgumentException("未设置SMTP UserName");
|
||
}
|
||
|
||
if (option == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(option));
|
||
}
|
||
|
||
if (option.MailTo == null || option.MailTo.Length < 1)
|
||
{
|
||
throw new ArgumentException("未设置收件人");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(option.Subject))
|
||
{
|
||
throw new ArgumentException("未设置邮件主题");
|
||
}
|
||
|
||
var from = option.From.TrimToNull() ?? smtpConfig.From.TrimToNull() ?? smtpConfig.UserName.TrimToNull() ?? throw new ArgumentNullException("未设置发件人");
|
||
|
||
var separator = new[] { ',', ';', ',', ';' };
|
||
|
||
var message = new MimeMessage();
|
||
message.From.Add(new MailboxAddress(string.Empty, from));
|
||
foreach (var mailTo in option.MailTo)
|
||
{
|
||
message.To.Add(new MailboxAddress(string.Empty, mailTo));
|
||
}
|
||
|
||
message.Subject = option.Subject;
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.CC))
|
||
{
|
||
var addrArr = option.CC.Split(separator, StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(n => new MailboxAddress(string.Empty, n.Trim()));
|
||
message.Cc.AddRange(addrArr);
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.BCC))
|
||
{
|
||
var addrArr = option.BCC.Split(separator, StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(n => new MailboxAddress(string.Empty, n.Trim()));
|
||
message.Bcc.AddRange(addrArr);
|
||
}
|
||
|
||
if (option.AdditionalHeaders != null)
|
||
{
|
||
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.HeaderEncoding))
|
||
{
|
||
message.Headers.Add(HeaderId.Encoding, option.HeaderEncoding.Trim());
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.Priority) &&
|
||
Enum.TryParse(option.Priority.Trim(), out MessagePriority prri))
|
||
{
|
||
message.Priority = prri;
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.ReplyTo))
|
||
{
|
||
message.ReplyTo.Add(new MailboxAddress(string.Empty, option.ReplyTo.Trim()));
|
||
}
|
||
|
||
var builder = new BodyBuilder();
|
||
if (option.IsBodyHtml)
|
||
{
|
||
builder.HtmlBody = option.Body;
|
||
}
|
||
else
|
||
{
|
||
builder.TextBody = option.Body;
|
||
}
|
||
|
||
if (option.FilesToAttach != null && option.FilesToAttach.Any(n => !string.IsNullOrWhiteSpace(n)))
|
||
{
|
||
var attaches = option.FilesToAttach.Where(n => !string.IsNullOrWhiteSpace(n)).ToArray();
|
||
|
||
for (var i = 0; i < attaches.Length; i++)
|
||
{
|
||
var attach = attaches[i];
|
||
var attachPath = attach;
|
||
if (attach.Contains("/")&& OperatingSystem.IsWindows())
|
||
{
|
||
attachPath = attach.Replace("/", "\\");
|
||
}
|
||
var attachments = builder.Attachments.Add(attachPath);
|
||
|
||
if (log != null)
|
||
{
|
||
var name = "邮件附件" + (i + 1);
|
||
log.Invoke(name, "源地址:" + attach);
|
||
log.Invoke(name, "解析地址:" + attachPath);
|
||
log.Invoke(name, "文件名称:" + attachments.ContentDisposition.FileName);
|
||
}
|
||
}
|
||
|
||
if (smtpConfig.ZipAttach)
|
||
{
|
||
using var zipStream = new MemoryStream();
|
||
using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Update))
|
||
{
|
||
foreach (var attach in attaches)
|
||
{
|
||
var fileInfo = new FileInfo(attach);
|
||
var entry = archive.CreateEntry(fileInfo.Name);
|
||
using var stream = entry.Open();
|
||
var bytes = File.ReadAllBytes(fileInfo.FullName);
|
||
stream.Write(bytes, 0, bytes.Length);
|
||
}
|
||
}
|
||
|
||
builder.Attachments.Add(option.Subject + ".zip", zipStream.ToArray());
|
||
log?.Invoke("邮件附件ZIP", "Subject:" + option.Subject);
|
||
}
|
||
}
|
||
|
||
message.Body = builder.ToMessageBody();
|
||
|
||
if (!string.IsNullOrWhiteSpace(option.ContentEncoding))
|
||
{
|
||
message.Body.Headers.Add(HeaderId.Encoding, option.ContentEncoding.Trim());
|
||
}
|
||
|
||
//以outlook名义发送邮件,不会被当作垃圾邮件
|
||
message.Headers.Add("X-Priority", "3");
|
||
message.Headers.Add("X-MSMail-Priority", "Normal");
|
||
message.Headers.Add("X-Mailer", "Microsoft Outlook Express 6.00.2900.2869");
|
||
message.Headers.Add("X-MimeOLE", "Produced By Microsoft MimeOLE V6.00.2900.2869");
|
||
message.Headers.Add("ReturnReceipt", "1");
|
||
|
||
if (smtpConfig.Receipt)
|
||
{
|
||
message.Headers.Add("Disposition-Notification-To", from);
|
||
}
|
||
|
||
using var client = new SmtpClient();
|
||
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
|
||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||
|
||
var userName = smtpConfig.UserName;
|
||
var secureSocketOptions = SecureSocketOptions.None;
|
||
|
||
//当做exchange邮件服务器处理
|
||
if (smtpConfig.Port == 587)
|
||
{
|
||
client.SslProtocols = System.Security.Authentication.SslProtocols.Tls;
|
||
if (smtpConfig.EnableSsl)
|
||
{
|
||
secureSocketOptions = SecureSocketOptions.StartTls;
|
||
}
|
||
var index = userName.IndexOf('@');
|
||
if (index > 0)
|
||
{
|
||
userName = userName.Substring(0, index);
|
||
}
|
||
}
|
||
else if (smtpConfig.EnableSsl)
|
||
{
|
||
secureSocketOptions = SecureSocketOptions.SslOnConnect;
|
||
}
|
||
|
||
client.Connect(smtpConfig.Server, smtpConfig.Port, secureSocketOptions);
|
||
|
||
// Note: since we don't have an OAuth2 token, disable the XOAUTH2 authentication mechanism.
|
||
client.AuthenticationMechanisms.Remove("XOAUTH2");
|
||
|
||
if (!string.IsNullOrWhiteSpace(smtpConfig.Password))
|
||
{
|
||
client.Authenticate(userName, smtpConfig.Password);
|
||
}
|
||
|
||
client.Send(message);
|
||
client.Disconnect(true);
|
||
}
|
||
|
||
public static string SendApi(MailSendingOption option, Action<string, string> log = null)
|
||
{
|
||
if (option == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(option));
|
||
}
|
||
|
||
if (option.MailTo == null || option.MailTo.Length < 1)
|
||
{
|
||
throw new ArgumentException("未设置收件人");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(option.Subject))
|
||
{
|
||
throw new ArgumentException("未设置邮件主题");
|
||
}
|
||
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
|
||
var emailPath = "/general/email/send";
|
||
if (string.IsNullOrEmpty(baseUrl))
|
||
{
|
||
throw new ArgumentException("未设置邮件接口地址");
|
||
}
|
||
// 创建一个HttpClient实例
|
||
using (var httpClient = new HttpClient())
|
||
{
|
||
httpClient.Timeout = TimeSpan.FromMinutes(3); // 根据需要调整
|
||
var content = new MultipartFormDataContent();
|
||
// 添加收件人
|
||
content.Add(new StringContent(string.Join(",", option.MailTo)), "to");
|
||
|
||
// 添加主题
|
||
content.Add(new StringContent(option.Subject), "subject");
|
||
|
||
// 添加邮件正文
|
||
content.Add(new StringContent(option.Body), "text");
|
||
|
||
// 添加邮件正文
|
||
content.Add(new StringContent(option.IsBodyHtml.ToString()), "isHtmlContent");
|
||
if (!string.IsNullOrWhiteSpace(option.CC))
|
||
{
|
||
content.Add(new StringContent(string.Join(",", option.CC)), "cc");
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(option.BCC))
|
||
{
|
||
content.Add(new StringContent(string.Join(",", option.BCC)), "bcc");
|
||
}
|
||
// 将文件添加到请求体中
|
||
if (option.FilesToAttach != null && option.FilesToAttach.Any(n => !string.IsNullOrWhiteSpace(n)))
|
||
{
|
||
var attaches = option.FilesToAttach.Where(n => !string.IsNullOrWhiteSpace(n)).ToArray();
|
||
|
||
for (var i = 0; i < attaches.Length; i++)
|
||
{
|
||
var attach = attaches[i];
|
||
var attachPath = attach;
|
||
if (attach.Contains("/") && OperatingSystem.IsWindows())
|
||
{
|
||
attachPath = attach.Replace("/", "\\");
|
||
}
|
||
var fileInfo= new FileInfo(attachPath);
|
||
// 添加附件
|
||
var attachmentContent = new ByteArrayContent(File.ReadAllBytes(attachPath));
|
||
content.Add(attachmentContent, "files", fileInfo.Name);
|
||
|
||
if (log != null)
|
||
{
|
||
var name = "邮件附件" + (i + 1);
|
||
log.Invoke(name, "源地址:" + attach);
|
||
log.Invoke(name, "解析地址:" + attachPath);
|
||
log.Invoke(name, "文件名称:" + fileInfo.Name);
|
||
}
|
||
}
|
||
}
|
||
if (log != null) {
|
||
log.Invoke("邮件发送接口", "BaseUrl:" + baseUrl);
|
||
log.Invoke("邮件发送接口", "EmailPath:" + emailPath);
|
||
}
|
||
// 发送POST请求
|
||
var response = httpClient.PostAsync($"{baseUrl}{emailPath}", content).Result;
|
||
|
||
// 检查响应状态码
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
// 解析响应内容并返回结果
|
||
var result = response.Content.ReadAsStringAsync().Result;
|
||
if (log != null)
|
||
{
|
||
log.Invoke("邮件发送接口", "响应内容:" + result);
|
||
}
|
||
if (string.IsNullOrEmpty(result))
|
||
{
|
||
throw new ArgumentException("发送邮件失败");
|
||
}
|
||
var trsRespone = JsonConvert.DeserializeObject<TrsRespone>(result);
|
||
if (!trsRespone.success)
|
||
{
|
||
throw new ArgumentException("发送邮件失败:"+ trsRespone.message);
|
||
}
|
||
return trsRespone.data.ToString();
|
||
}
|
||
else
|
||
{
|
||
if (log != null)
|
||
{
|
||
log.Invoke("邮件发送接口", "响应状态码:" + response.StatusCode);
|
||
log.Invoke("邮件发送接口", "响应内容:" + response.Content.ReadAsStringAsync().Result);
|
||
}
|
||
// 处理错误情况
|
||
}
|
||
}
|
||
return "";
|
||
}
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
static string TrimToNull(this string str)
|
||
{
|
||
return string.IsNullOrWhiteSpace(str) ? null : str.Trim();
|
||
}
|
||
}
|
||
}
|