using MailKit.Net.Smtp; using MailKit.Security; using MimeKit; using Newtonsoft.Json; using System.IO.Compression; namespace YLErp.MailKit { public static class MailSender { /// /// 发送邮件 /// public static void Send(SmtpConfig smtpConfig, MailSendingOption option, Action 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 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(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 ""; } /// /// /// static string TrimToNull(this string str) { return string.IsNullOrWhiteSpace(str) ? null : str.Trim(); } } }