System.Net.Mail 是 .NET Framework 2.0 起取代舊版 System.Web.Mail 的標準郵件發送庫。本文提供一個可直接用於生產環境的 EmailService 類別,涵蓋 SSL/TLS 加密、HTML 內容、附件、嵌入圖片、多收件人、重試與日誌記錄等實務需求。
using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading.Tasks;
public sealed class EmailService : IDisposable
{
private readonly SmtpClient _client;
private readonly string _fromAddress;
private readonly string _fromDisplayName;
public EmailService(string smtpHost, int port, string userName, string password,
string fromAddress, string fromDisplayName = null, bool enableSsl = true)
{
_fromAddress = fromAddress ?? throw new ArgumentNullException(nameof(fromAddress));
_fromDisplayName = fromDisplayName;
_client = new SmtpClient(smtpHost, port)
{
Credentials = new NetworkCredential(userName, password),
EnableSsl = enableSsl,
DeliveryMethod = SmtpDeliveryMethod.Network,
Timeout = 30000 // 30 秒
};
}
/// 非同步發送郵件
public async Task SendAsync(EmailMessage message)
{
using (var mail = BuildMailMessage(message))
await _client.SendMailAsync(mail).ConfigureAwait(false);
}
/// 同步發送(適用於舊版專案)
public void Send(EmailMessage message)
{
using (var mail = BuildMailMessage(message))
_client.Send(mail);
}
private MailMessage BuildMailMessage(EmailMessage msg)
{
var mail = new MailMessage
{
From = new MailAddress(_fromAddress, _fromDisplayName),
Subject = msg.Subject,
Body = msg.Body,
IsBodyHtml = msg.IsHtml,
BodyEncoding = System.Text.Encoding.UTF8,
Priority = msg.Priority
};
// 收件人
msg.To.ForEach(t => mail.To.Add(t));
msg.Cc.ForEach(c => mail.CC.Add(c));
msg.Bcc.ForEach(b => mail.Bcc.Add(b));
// 附件
foreach (var att in msg.Attachments)
{
if (File.Exists(att.FilePath))
mail.Attachments.Add(new Attachment(att.FilePath, att.MediaType));
else if (att.Stream != null)
mail.Attachments.Add(new Attachment(att.Stream, att.FileName, att.MediaType));
}
// 嵌入圖片 (cid:)
foreach (var inline in msg.InlineImages)
{
var lr = new LinkedResource(inline.Stream ?? File.OpenRead(inline.FilePath), inline.MediaType)
{
ContentId = inline.ContentId,
TransferEncoding = TransferEncoding.Base64
};
mail.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(
msg.Body, null, MediaTypeNames.Text.Html));
// 實務上需將 LinkedResource 加入 AlternateView 的 LinkedResources 集合
// 此處略過完整 AlternateView 建構,請參考官方文件
}
return mail;
}
public void Dispose() => _client?.Dispose();
}
/// 郵件訊息模型
public sealed class EmailMessage
{
public string Subject { get; set; }
public string Body { get; set; }
public bool IsHtml { get; set; } = true;
public MailPriority Priority { get; set; } = MailPriority.Normal;
public List To { get; } = new List();
public List Cc { get; } = new List();
public List Bcc { get; } = new List();
public List Attachments { get; } = new List();
public List InlineImages { get; } = new List();
}
public sealed class EmailAttachment
{
public string FilePath { get; set; }
public Stream Stream { get; set; }
public string FileName { get; set; }
public string MediaType { get; set; } = MediaTypeNames.Application.Octet;
}
public sealed class InlineImage
{
public string ContentId { get; set; } // 對應 HTML 中 <img src="cid:xxx" />
public string FilePath { get; set; }
public Stream Stream { get; set; }
public string MediaType { get; set; } = MediaTypeNames.Image.Jpeg;
}
// 設定(建議從 appsettings.json / 環境變數讀取)
var svc = new EmailService(
smtpHost: "smtp.gmail.com", port: 587,
userName: "your@gmail.com", password: "app-password",
fromAddress: "your@gmail.com", fromDisplayName: "我的系統");
var msg = new EmailMessage
{
Subject = "測試郵件 " + DateTime.Now,
Body = @"<h1>HTML 內容</h1><p>支援中文</p><img src='cid:logo' />",
IsHtml = true,
Priority = MailPriority.High
};
msg.To.Add("target@example.com");
msg.Attachments.Add(new EmailAttachment { FilePath = @"C:\report.pdf" });
msg.InlineImages.Add(new InlineImage { ContentId = "logo", FilePath = @"C:\logo.png" });
await svc.SendAsync(msg); // .NET 4.5+ 非同步
// svc.Send(msg); // 同步版本
| 問題 | 原因 | 解決 |
|---|---|---|
| 「郵箱不可用」/ 5.7.0/5.7.1 | Gmail/Outlook 關閉低安全性應用程式存取 | 啟用「兩步驟驗證」→ 產生「應用程式密碼」或使用 OAuth 2.0 (XOAUTH2) |
| 附件檔名中文亂碼 | RFC 2231 編碼未正確處理 | 使用 Attachment.NameEncoding = Encoding.UTF8 或升級 .NET 6+ |
| 大附件逾時 | 預設 Timeout 100秒 / 伺服器限制 | 調大 SmtpClient.Timeout、分片上傳或改用雲端儲存連結 |
| SSL/TLS 協商失敗 | 目標伺服器強制 TLS 1.2+ | 程式啟動時加 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; |
SmtpClient 連線池,配合 SemaphoreSlim 限制並發數(建議 5-10),避免觸發反垃圾郵件限流。MailKit 套件實作。封裝可重用的 EmailService、定義強型別 EmailMessage、妥善處理編碼與 SSL/TLS,即可構建穩健、可維護的郵件發送模組。建議新專案直接採用 MailKit(跨平台、支援 OAuth、IMAP/POP3/SMTP 全功能),僅在維護舊版 .NET Framework 專案時使用 System.Net.Mail。