C#郵件發(fā)送與附件處理過程詳解
郵件發(fā)送基礎(chǔ)概念
什么是郵件發(fā)送?
郵件發(fā)送是通過網(wǎng)絡(luò)將電子信息從一個用戶發(fā)送到另一個用戶的過程。在編程中,我們通常使用SMTP 協(xié)議來實現(xiàn)郵件發(fā)送功能。
郵件發(fā)送的基本流程
發(fā)件人 → SMTP服務(wù)器 → 收件人郵件服務(wù)器 → 收件人
核心組成部分
1. 發(fā)件人信息:郵箱地址、顯示名稱? 2. 收件人信息:郵箱地址(可以多個)? 3. 郵件內(nèi)容:主題、正文? 4. 附件:可選的文件附加? 5. SMTP 服務(wù)器配置:服務(wù)器地址、端口、認證信息
SMTP 協(xié)議簡介
什么是 SMTP?
**SMTP (Simple Mail Transfer Protocol)** 是簡單郵件傳輸協(xié)議,是用于發(fā)送電子郵件的標(biāo)準(zhǔn)協(xié)議。
常見 SMTP 服務(wù)器配置
| 服務(wù)商 | SMTP 服務(wù)器 | 端口 | SSL |
|---|---|---|---|
| 阿里云 | smtpdm.aliyun.com | 80/465 | 80 端口不使用 SSL,465 端口使用 SSL |
| 騰訊企業(yè)郵 | smtp.exmail.qq.com | 465 | 是 |
為什么需要認證?
SMTP 服務(wù)器通常需要用戶名和密碼認證,以防止垃圾郵件發(fā)送和未授權(quán)使用。
C# 郵件發(fā)送核心類庫
System.Net.Mail 命名空間?
C# 提供了專門的命名空間用于郵件發(fā)送:
using System.Net; using System.Net.Mail; using System.Net.Mime;
核心類介紹
1. MailMessage 類?
用于構(gòu)建郵件消息的內(nèi)容和結(jié)構(gòu)。
MailMessage mail = new MailMessage();
mail.From = new MailAddress("sender@example.com", "發(fā)件人顯示名稱");
mail.To.Add("recipient@example.com");
mail.Subject = "郵件主題";
mail.Body = "郵件正文內(nèi)容";
mail.IsBodyHtml = true; // 是否支持HTML格式
2. SmtpClient 類?
用于通過 SMTP 服務(wù)器發(fā)送郵件。
SmtpClient smtpClient = new SmtpClient("smtp.server.com", 587);
smtpClient.Credentials = new NetworkCredential("username", "password");
smtpClient.EnableSsl = true;
smtpClient.Send(mail);
3. Attachment 類
Attachment attachment = new Attachment("file.txt");
mail.Attachments.Add(attachment);
基礎(chǔ)郵件發(fā)送實現(xiàn)
最簡單的郵件發(fā)送示例
using System;
using System.Net;
using System.Net.Mail;
class SimpleEmailSender
{
static void Main(string[] args)
{
try
{
// 1. 創(chuàng)建郵件消息
MailMessage mail = new MailMessage();
mail.From = new MailAddress("your-email@example.com", "你的名稱");
mail.To.Add("recipient@example.com");
mail.Subject = "測試郵件";
mail.Body = "這是一封測試郵件!";
mail.IsBodyHtml = false;
// 2. 配置SMTP客戶端
SmtpClient smtpClient = new SmtpClient("smtp.server.com", 587);
smtpClient.Credentials = new NetworkCredential("your-email@example.com", "your-password");
smtpClient.EnableSsl = true;
// 3. 發(fā)送郵件
smtpClient.Send(mail);
Console.WriteLine("郵件發(fā)送成功!");
}
catch (Exception ex)
{
Console.WriteLine($"郵件發(fā)送失敗: {ex.Message}");
}
}
}
關(guān)鍵代碼解釋?
- MailMessage 對象創(chuàng)建:構(gòu)建郵件的基本信息?
- SmtpClient 配置:設(shè)置 SMTP 服務(wù)器信息和認證?
- 異常處理:捕獲可能的發(fā)送錯誤
業(yè)務(wù)中完整的郵件發(fā)送工具類封裝
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Text;
namespace Tools.Mail
{
/// <summary>
/// SMTP郵件發(fā)送器,支持發(fā)送帶附件的郵件
/// </summary>
public static class SmtpEmailHelper
{
#region 配置參數(shù)
// SMTP服務(wù)器配置
private const string SmtpServer = " ";
private const int SmtpPort = 80;
private const bool EnableSsl = false;
// 發(fā)件人信息
private const string FromAddress = "system@example.com";
private const string FromDisplayName = "系統(tǒng)通知";
private const string SmtpUsername = "system@example.com";
private const string SmtpPassword = "your-password";
// 附件配置
private const int MaxAttachmentSize = 8 * 1024 * 1024; // 單個附件最大8MB
private const int MaxTotalAttachmentSize = 12 * 1024 * 1024; // 總附件大小限制
#endregion
#region 附件數(shù)據(jù)結(jié)構(gòu)
/// <summary>
/// 郵件附件信息
/// </summary>
public class EmailAttachment
{
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 文件內(nèi)容(字節(jié)數(shù)組)
/// </summary>
public byte[] Content { get; set; }
/// <summary>
/// 內(nèi)容類型
/// </summary>
public string ContentType { get; set; }
}
#endregion
#region 發(fā)送郵件方法
/// <summary>
/// 發(fā)送帶附件的郵件
/// </summary>
/// <param name="toAddress">收件人地址(多個用逗號分隔)</param>
/// <param name="subject">郵件主題</param>
/// <param name="htmlBody">HTML郵件正文</param>
/// <param name="attachments">附件列表</param>
public static void SendEmailWithAttachments(string toAddress, string subject,
string htmlBody, List<EmailAttachment> attachments = null)
{
try
{
// 創(chuàng)建郵件消息
MailMessage mail = CreateMailMessage(toAddress, subject, htmlBody);
// 添加附件
if (attachments != null && attachments.Any())
{
AddAttachmentsToMail(mail, attachments);
}
// 發(fā)送郵件
SendMail(mail);
}
catch (Exception ex)
{
Console.WriteLine($"郵件發(fā)送失敗: {ex.Message}");
throw;
}
}
#endregion
#region 郵件創(chuàng)建
private static MailMessage CreateMailMessage(string toAddress, string subject, string htmlBody)
{
MailMessage mail = new MailMessage();
// 發(fā)件人
mail.From = new MailAddress(FromAddress, FromDisplayName, Encoding.UTF8);
// 收件人
foreach (string receiver in toAddress.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
mail.To.Add(new MailAddress(receiver.Trim()));
}
// 郵件主題
mail.Subject = subject;
mail.SubjectEncoding = Encoding.UTF8;
// 郵件正文(HTML格式)
mail.Body = htmlBody;
mail.BodyEncoding = Encoding.UTF8;
mail.IsBodyHtml = true;
// 優(yōu)先級
mail.Priority = MailPriority.High;
return mail;
}
#endregion
#region 附件處理
private static void AddAttachmentsToMail(MailMessage mail, List<EmailAttachment> attachments)
{
long totalAttachmentSize = 0;
foreach (var attachment in attachments)
{
if (attachment == null || attachment.Content == null || attachment.Content.Length == 0)
continue;
// 檢查單個附件大小
if (attachment.Content.Length > MaxAttachmentSize)
{
Console.WriteLine($"附件 {attachment.FileName} 太大,跳過添加");
continue;
}
// 檢查總附件大小
if (totalAttachmentSize + attachment.Content.Length > MaxTotalAttachmentSize)
{
Console.WriteLine($"總附件大小超出限制,跳過添加附件 {attachment.FileName}");
break;
}
try
{
// 創(chuàng)建附件
Attachment mailAttachment = new Attachment(
new MemoryStream(attachment.Content),
attachment.FileName,
attachment.ContentType ?? "application/octet-stream"
);
// 設(shè)置附件屬性
ContentDisposition disposition = mailAttachment.ContentDisposition;
disposition.CreationDate = DateTime.Now;
disposition.ModificationDate = DateTime.Now;
disposition.ReadDate = DateTime.Now;
disposition.FileName = attachment.FileName;
disposition.Size = attachment.Content.Length;
disposition.DispositionType = DispositionTypeNames.Attachment;
mail.Attachments.Add(mailAttachment);
totalAttachmentSize += attachment.Content.Length;
Console.WriteLine($"添加附件成功: {attachment.FileName}");
}
catch (Exception ex)
{
Console.WriteLine($"添加附件 {attachment.FileName} 失敗: {ex.Message}");
}
}
}
#endregion
#region 郵件發(fā)送
private static void SendMail(MailMessage mail)
{
using (SmtpClient smtpClient = new SmtpClient(SmtpServer, SmtpPort))
{
smtpClient.EnableSsl = EnableSsl;
smtpClient.Credentials = new NetworkCredential(SmtpUsername, SmtpPassword);
smtpClient.Timeout = 300000; // 5分鐘超時
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
// 發(fā)送郵件
smtpClient.Send(mail);
}
Console.WriteLine($"郵件發(fā)送成功,收件人: {string.Join(",", mail.To.Select(t => t.Address))}");
}
#endregion
}
}
封裝的優(yōu)勢?
- 職責(zé)單一:每個方法只負責(zé)一個具體功能?
- 錯誤處理:統(tǒng)一的異常處理機制?
- 配置集中:所有配置參數(shù)集中管理?
- 易于使用:簡潔的公共接口
實際業(yè)務(wù)應(yīng)用
業(yè)務(wù)場景分析
以企業(yè)批次處理報告郵件為例,我們需要:?
- 準(zhǔn)備業(yè)務(wù)數(shù)據(jù)?
- 生成郵件內(nèi)容?
- 創(chuàng)建附件(報告文件)?
- 發(fā)送郵件
業(yè)務(wù)數(shù)據(jù)模型設(shè)計
/// <summary>
/// 批次報告數(shù)據(jù)模型
/// 包含所有生成報告所需的業(yè)務(wù)數(shù)據(jù)
/// </summary>
public class BatchReportData
{
public int Batch { get; set; } // 批次號
public int Year { get; set; } // 年份
public int TotalCount { get; set; } // 總企業(yè)數(shù)
public int SuccessCount { get; set; } // 成功處理數(shù)
public int ErrorCount { get; set; } // 錯誤數(shù)
public int UnobtainedCount { get; set; } // 未獲取數(shù)
public List<BatchRecordModel> AbnormalRecords { get; set; } // 異常記錄
public List<BatchRecordModel> UnobtainedRecords { get; set; } // 未獲取記錄
public List<string> ErrorLogs { get; set; } // 錯誤日志
public string Receivers { get; set; } // 收件人列表
public DateTime GenerateTime { get; set; } // 生成時間
}
業(yè)務(wù)邏輯實現(xiàn)
1. 數(shù)據(jù)準(zhǔn)備
/// <summary>
/// 準(zhǔn)備批次報告所需的業(yè)務(wù)數(shù)據(jù)
/// </summary>
private BatchReportData PrepareBatchReportData(int batch, List<string> errors)
{
// 1. 獲取當(dāng)前年份
int year = DateTime.Now.Year;
// 2. 查詢批次記錄
List<BatchRecordModel> batchRecords = _enterpriseProvider.GetBatchRecords(batch, year);
if (!batchRecords.Any())
{
Logger.Warn($"批次{batch}年度{year}沒有找到記錄");
return null;
}
// 3. 統(tǒng)計數(shù)據(jù)
int totalCount = batchRecords.Count;
int successCount = batchRecords.Count(r => r.Status == 1);
int errorCount = batchRecords.Count(r => r.Status == 2);
int unobtainedCount = batchRecords.Count(r => r.Status == 0);
// 4. 獲取異常和未獲取記錄
List<BatchRecordModel> abnormalRecords = batchRecords.Where(r => r.HasAbnormalData == 1).ToList();
List<BatchRecordModel> unobtainedRecords = batchRecords.Where(r => r.Status == 0).ToList();
// 5. 獲取收件人配置
string receivers = _configProvider.GetEmailReceivers();
return new BatchReportData
{
Batch = batch,
Year = year,
TotalCount = totalCount,
SuccessCount = successCount,
ErrorCount = errorCount,
UnobtainedCount = unobtainedCount,
AbnormalRecords = abnormalRecords,
UnobtainedRecords = unobtainedRecords,
ErrorLogs = errors ?? new List<string>(),
Receivers = receivers,
GenerateTime = DateTime.Now
};
}
2. HTML 郵件內(nèi)容生成
/// <summary>
/// 生成HTML格式的郵件正文
/// </summary>
private string GenerateEmailHtmlBody(BatchReportData reportData)
{
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.AppendLine("<!DOCTYPE html>");
htmlBuilder.AppendLine("<html lang='zh-CN'>");
htmlBuilder.AppendLine("<head>");
htmlBuilder.AppendLine("<meta charset='UTF-8'>");
htmlBuilder.AppendLine("<title>批次處理結(jié)果報告</title>");
htmlBuilder.AppendLine("<style>");
htmlBuilder.AppendLine("body { font-family: 'Microsoft YaHei', Arial, sans-serif; line-height: 1.6; }");
htmlBuilder.AppendLine(".container { max-width: 800px; margin: 0 auto; padding: 20px; }");
htmlBuilder.AppendLine(".header { background-color: #f5f5f5; padding: 15px; text-align: center; border-radius: 5px; }");
htmlBuilder.AppendLine(".content { margin: 20px 0; }");
htmlBuilder.AppendLine(".stats { background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin: 15px 0; }");
htmlBuilder.AppendLine(".stat-item { margin: 8px 0; }");
htmlBuilder.AppendLine(".footer { text-align: center; margin-top: 30px; color: #666; font-size: 14px; }");
htmlBuilder.AppendLine("</style>");
htmlBuilder.AppendLine("</head>");
htmlBuilder.AppendLine("<body>");
htmlBuilder.AppendLine("<div class='container'>");
// 郵件頭部
htmlBuilder.AppendLine("<div class='header'>");
htmlBuilder.AppendLine($"<h2>批次處理結(jié)果報告 - 批次{reportData.Batch} ({reportData.Year}年度)</h2>");
htmlBuilder.AppendLine("</div>");
// 處理時間
htmlBuilder.AppendLine("<div class='content'>");
htmlBuilder.AppendLine($"<p>報告生成時間:{reportData.GenerateTime.ToString("yyyy-MM-dd HH:mm:ss")}</p>");
// 統(tǒng)計信息
htmlBuilder.AppendLine("<div class='stats'>");
htmlBuilder.AppendLine("<h3>處理統(tǒng)計</h3>");
htmlBuilder.AppendLine($"<div class='stat-item'>總企業(yè)數(shù):<strong>{reportData.TotalCount}</strong></div>");
htmlBuilder.AppendLine($"<div class='stat-item'>成功處理:<strong>{reportData.SuccessCount}</strong></div>");
htmlBuilder.AppendLine($"<div class='stat-item'>處理失?。?lt;strong>{reportData.ErrorCount}</strong></div>");
htmlBuilder.AppendLine($"<div class='stat-item'>未獲取信息:<strong>{reportData.UnobtainedCount}</strong></div>");
htmlBuilder.AppendLine("</div>");
// 異常企業(yè)信息
if (reportData.AbnormalRecords.Any())
{
htmlBuilder.AppendLine("<div class='stats'>");
htmlBuilder.AppendLine($"<h3>異常企業(yè) ({reportData.AbnormalRecords.Count}家)</h3>");
htmlBuilder.AppendLine("<p>以下企業(yè)存在經(jīng)營異常或嚴(yán)重違法記錄,請及時處理。</p>");
htmlBuilder.AppendLine("</div>");
}
// 郵件底部
htmlBuilder.AppendLine("<div class='footer'>");
htmlBuilder.AppendLine("<p>此郵件為系統(tǒng)自動發(fā)送,請勿回復(fù)。詳細信息請查看附件。</p>");
htmlBuilder.AppendLine("</div>");
htmlBuilder.AppendLine("</div>");
htmlBuilder.AppendLine("</div>");
htmlBuilder.AppendLine("</body>");
htmlBuilder.AppendLine("</html>");
return htmlBuilder.ToString();
}
3. 附件生成
/// <summary>
/// 生成報告附件
/// </summary>
private List<SmtpEmailHelper.EmailAttachment> GenerateReportAttachments(BatchReportData reportData)
{
List<SmtpEmailHelper.EmailAttachment> attachments = new List<SmtpEmailHelper.EmailAttachment>();
// 生成CSV格式的詳細報告
byte[] csvContent = GenerateCsvReport(reportData);
attachments.Add(new SmtpEmailHelper.EmailAttachment
{
FileName = $"批次{reportData.Batch}_處理報告_{reportData.GenerateTime:yyyyMMddHHmmss}.csv",
Content = csvContent,
ContentType = "text/csv"
});
// 生成錯誤日志文件
if (reportData.ErrorLogs.Any())
{
byte[] errorLogContent = GenerateErrorLogFile(reportData);
attachments.Add(new SmtpEmailHelper.EmailAttachment
{
FileName = $"批次{reportData.Batch}_錯誤日志_{reportData.GenerateTime:yyyyMMddHHmmss}.txt",
Content = errorLogContent,
ContentType = "text/plain"
});
}
return attachments;
}
/// <summary>
/// 生成CSV格式報告
/// </summary>
private byte[] GenerateCsvReport(BatchReportData reportData)
{
using (MemoryStream ms = new MemoryStream())
using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
{
// CSV頭部
writer.WriteLine("企業(yè)名稱,統(tǒng)一社會信用代碼,處理狀態(tài),異常類型,獲取時間,備注");
// 處理數(shù)據(jù)行
foreach (var record in reportData.AbnormalRecords)
{
string status = record.Status == 1 ? "成功" : record.Status == 2 ? "失敗" : "未處理";
string abnormalType = record.HasRegStatusAbnormal == 1 ? "經(jīng)營異常" :
record.HasIllegalData == 1 ? "嚴(yán)重違法" : "其他異常";
writer.WriteLine($"{CsvEncode(record.FacName)},{CsvEncode(record.FacCode)},{status},{abnormalType},{record.UpdateTime:yyyy-MM-dd HH:mm:ss},");
}
writer.Flush();
return ms.ToArray();
}
}
/// <summary>
/// 生成錯誤日志文件
/// </summary>
private byte[] GenerateErrorLogFile(BatchReportData reportData)
{
using (MemoryStream ms = new MemoryStream())
using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
{
writer.WriteLine($"批次{reportData.Batch}錯誤日志");
writer.WriteLine($"生成時間:{reportData.GenerateTime:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"錯誤總數(shù):{reportData.ErrorLogs.Count}");
writer.WriteLine();
writer.WriteLine("錯誤詳情:");
writer.WriteLine();
for (int i = 0; i < reportData.ErrorLogs.Count; i++)
{
writer.WriteLine($"錯誤 {i + 1}:");
writer.WriteLine(reportData.ErrorLogs[i]);
writer.WriteLine();
writer.WriteLine("-".PadRight(60, '-'));
writer.WriteLine();
}
writer.Flush();
return ms.ToArray();
}
}
/// <summary>
/// CSV編碼處理
/// </summary>
private string CsvEncode(string value)
{
if (string.IsNullOrEmpty(value))
return string.Empty;
// CSV編碼規(guī)則:如果包含逗號、引號或換行符,需要用雙引號括起來
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
{
return "\"" + value.Replace("\"", "\"\"") + "\"";
}
return value;
}
4. 業(yè)務(wù)方法調(diào)用
/// <summary>
/// 發(fā)送批次處理結(jié)果郵件報告(帶附件版本)
/// 業(yè)務(wù)邏輯:準(zhǔn)備數(shù)據(jù) -> 生成內(nèi)容 -> 調(diào)用郵件發(fā)送器
/// </summary>
public void SendBatchProcessNotification(int batch, List<string> errors)
{
try
{
// 1. 業(yè)務(wù)數(shù)據(jù)準(zhǔn)備
var reportData = PrepareBatchReportData(batch, errors);
if (reportData == null)
{
Logger.Warn($"批次{batch}沒有數(shù)據(jù)需要發(fā)送");
return;
}
// 2. 生成郵件內(nèi)容(業(yè)務(wù)邏輯)
string emailSubject = $"批次{reportData.Batch}處理結(jié)果報告 - {reportData.Year}年度";
string htmlBody = GenerateEmailHtmlBody(reportData);
// 3. 生成附件(業(yè)務(wù)邏輯)
var attachments = GenerateReportAttachments(reportData);
// 4. 調(diào)用獨立的郵件發(fā)送器(不包含業(yè)務(wù)邏輯)
SendEmailThroughSmtp(reportData.Receivers, emailSubject, htmlBody, attachments);
Logger.Info($"批次{batch}帶附件處理結(jié)果郵件已發(fā)送");
}
catch (Exception ex)
{
Logger.Error($"發(fā)送批次{batch}帶附件郵件報告失敗: {ex.Message}", ex);
}
}
/// <summary>
/// 通過SMTP發(fā)送郵件(僅負責(zé)調(diào)用,不包含業(yè)務(wù)邏輯)
/// </summary>
private void SendEmailThroughSmtp(string receivers, string subject, string htmlBody,
List<SmtpEmailHelper.EmailAttachment> attachments)
{
try
{
// 直接調(diào)用獨立的郵件發(fā)送器
SmtpEmailHelper.SendEmailWithAttachments(receivers, subject, htmlBody, attachments);
}
catch (Exception ex)
{
Logger.Error($"SMTP郵件發(fā)送器調(diào)用失敗: {ex.Message}", ex);
throw;
}
}
5. 最終結(jié)果

以上就是C#郵件發(fā)送與附件處理過程詳解的詳細內(nèi)容,更多關(guān)于C#郵件發(fā)送與附件處理的資料請關(guān)注腳本之家其它相關(guān)文章!

