上一篇介紹了透過反射呼叫內部 MailWriter 將 MailMessage 序列化為 EML,雖可行但程式碼複雜且依賴非公開 API。本文發現 SmtpClient 內建一種更簡單的機制:設定 DeliveryMethod = SpecifiedPickupDirectory,郵件不會真正發送,而是直接寫入指定目錄成 .eml 檔。
SmtpClient.Send 內部針對 DeliveryMethod 分派:
Network:透過 SMTP 協定發送PickupDirectoryFromIis:交給 IIS SMTP 服務拾取(需安裝 IIS)SpecifiedPickupDirectory:直接在 PickupDirectoryLocation 目錄生成 .eml 檔,無任何網路互動此機制原本設計給 IIS SMTP 服務輪詢拾取,我們借用它來「離線生成 EML」。
using System.Net.Mail;
public void SaveMailAsEml(string pickupFolder)
{
// 確保目錄存在
if (!Directory.Exists(pickupFolder))
Directory.CreateDirectory(pickupFolder);
using (var smtp = new SmtpClient())
{
smtp.EnableSsl = false; // 必須關閉 SSL
smtp.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
smtp.PickupDirectoryLocation = pickupFolder; // 例如 @"D:\MailDrop\"
var msg = new MailMessage();
msg.Subject = "Hello from C#";
msg.From = new MailAddress("sender@example.com");
msg.To.Add("recipient@example.com");
msg.Body = "這是透過 Pickup 目錄生成的 .eml 檔案。";
msg.IsBodyHtml = false;
smtp.Send(msg); // 瞬間在 pickupFolder 產生一個 {GUID}.eml
}
}
| 優點 | 缺點 |
|---|---|
| 零反射、零內部 API 依賴 | 檔名為隨機 GUID,無法自訂 |
| 程式碼極簡,幾行搞定 | 需手動掃描目錄取得最新檔案 |
| 自動處理附件、編碼、標頭格式 | 若需精確控制檔名,仍需反射法或自行組裝 MIME |
| 不需安裝/設定 SMTP 服務 | 大量生成時需注意目錄清理 |
public string SaveMailAsEmlAndGetPath(string pickupFolder, MailMessage msg)
{
// 發送前先記錄目錄現有檔案
var before = new HashSet<string>(Directory.GetFiles(pickupFolder, "*.eml"), StringComparer.OrdinalIgnoreCase);
using (var smtp = new SmtpClient())
{
smtp.EnableSsl = false;
smtp.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
smtp.PickupDirectoryLocation = pickupFolder;
smtp.Send(msg);
}
// 發送後比對新增檔案
var after = Directory.GetFiles(pickupFolder, "*.eml");
foreach (var f in after)
{
if (!before.Contains(f))
return f; // 回傳新生成的 .eml 完整路徑
}
return null;
}
原文出處:cnblogs sjcatsoft