C#实现企业级邮件发送功能与附件处理 1. 项目概述在当今企业级应用开发中邮件发送功能几乎是每个系统必备的基础模块。作为.NET生态中的主力语言C#提供了强大而灵活的邮件处理能力。这个开源实现不仅支持基础邮件发送还完整实现了附件处理功能可以直接集成到各类业务系统中。我曾在多个电商和OA系统中实现过邮件模块发现虽然.NET自带SMTP库但要处理复杂的附件场景如大文件分卷、格式转换仍需大量封装。这个项目正好解决了这些痛点其核心价值在于支持多种附件类型文档、图片、压缩包等内置异常重试机制提供发送进度回调兼容主流邮件服务商QQ、163、企业邮箱等2. 核心功能解析2.1 邮件基础配置使用.NET的System.Net.Mail命名空间构建基础发送功能var message new MailMessage { From new MailAddress(senderexample.com), Subject 带附件的测试邮件, Body h1这是HTML正文/h1, IsBodyHtml true }; message.To.Add(recipientexample.com); var smtp new SmtpClient(smtp.example.com) { Port 587, Credentials new NetworkCredential(username, password), EnableSsl true };关键参数说明PortSSL加密通常用587非加密用25EnableSsl必须与服务商配置一致Credentials建议使用SecureString存储密码2.2 附件处理机制附件功能通过Attachment类实现核心代码如下// 添加本地文件附件 var attachment new Attachment(report.pdf, MediaTypeNames.Application.Pdf); attachment.ContentDisposition.CreationDate File.GetCreationTime(filePath); attachment.ContentDisposition.ModificationDate File.GetLastWriteTime(filePath); message.Attachments.Add(attachment); // 处理内存流附件如动态生成的PDF using (var stream new MemoryStream(pdfBytes)) { var memoryAttachment new Attachment(stream, invoice.pdf); message.Attachments.Add(memoryAttachment); }重要提示大文件附件10MB建议先压缩再发送否则可能被邮件服务器拒绝3. 高级功能实现3.1 发送进度监控通过自定义SmtpClient扩展进度反馈public class ProgressSmtpClient : SmtpClient { public event Actionint ProgressChanged; protected override void OnSendCompleted(AsyncCompletedEventArgs e) { if (e.UserState is ProgressData data) { ProgressChanged?.Invoke(data.BytesSent); } base.OnSendCompleted(e); } } // 使用示例 var client new ProgressSmtpClient(); client.ProgressChanged percent { Console.WriteLine($已发送{percent}%); };3.2 邮件模板引擎结合RazorEngine实现动态内容string template h1尊敬的Model.UserName/h1 p您的订单Model.OrderId已发货/p; var model new { UserName 张三, OrderId 10086 }; string htmlBody Engine.Razor.RunCompile(template, orderTemplate, null, model); message.Body htmlBody;4. 企业级解决方案4.1 邮件队列服务高并发场景建议使用队列// 使用Channel实现内存队列 var mailQueue Channel.CreateUnboundedMailMessage(); // 生产者 async Task EnqueueMailAsync(MailMessage message) { await mailQueue.Writer.WriteAsync(message); } // 消费者 async Task ProcessQueueAsync() { await foreach (var message in mailQueue.Reader.ReadAllAsync()) { try { await smtp.SendMailAsync(message); message.Dispose(); // 及时释放资源 } catch (Exception ex) { // 记录日志并重试 } } }4.2 邮件发送策略根据业务需求选择不同策略策略类型适用场景实现要点立即发送验证码等实时邮件直接调用SendMailAsync定时批量日报/周报Hangfire定时任务失败重试重要通知Polly重试策略优先级队列混合业务多Channel分级处理5. 安全与性能优化5.1 安全实践凭证管理// 使用Windows凭据管理器 var cred new NetworkCredential( userName: ConfigurationManager.AppSettings[MailUser], password: SecureStringHelper.ToSecureString( ConfigurationManager.AppSettings[MailPassword]) );附件安全检查bool IsSafeAttachment(string filePath) { var allowedExtensions new[] { .pdf, .docx, .xlsx }; var extension Path.GetExtension(filePath).ToLower(); return allowedExtensions.Contains(extension) new FileInfo(filePath).Length 10 * 1024 * 1024; }5.2 性能调优连接池配置ServicePointManager.DefaultConnectionLimit 20; ServicePointManager.Expect100Continue false;异步发送最佳实践public async Task SendBatchAsync(IEnumerableMailMessage messages) { var tasks messages.Select(msg { using (msg) { // 确保资源释放 return smtp.SendMailAsync(msg); } }); await Task.WhenAll(tasks); }6. 常见问题排查6.1 典型错误对照表错误现象可能原因解决方案5.7.1 Client was not authenticated认证失败检查SSL配置和密码Transaction failed. The server response was: 5.2.1 Mailbox full收件箱已满联系收件人清理421 Too many connections连接数超限增加连接间隔552 Message size exceeds limit附件过大分卷压缩6.2 日志记录实现建议使用Serilog记录详细日志Log.Logger new LoggerConfiguration() .WriteTo.File(mail_log.txt, rollingInterval: RollingInterval.Day) .CreateLogger(); try { await smtp.SendMailAsync(message); Log.Information(邮件发送成功 {To}, message.To); } catch (SmtpException ex) { Log.Error(ex, 邮件发送失败 {Message}, message.Subject); }7. 完整实现示例以下是整合所有功能的完整类public class AdvancedMailSender { private readonly SmtpClient _client; private readonly ILogger _logger; public AdvancedMailSender(SmtpConfig config, ILogger logger) { _client new SmtpClient(config.Host) { Port config.Port, Credentials new NetworkCredential(config.UserName, config.Password), EnableSsl config.UseSsl, Timeout 30000 }; _logger logger; } public async Task SendEmailAsync(MailRequest request) { using var message new MailMessage { From new MailAddress(request.From), Subject request.Subject, Body request.Body, IsBodyHtml request.IsHtml }; foreach (var to in request.To) { message.To.Add(to); } foreach (var attachment in request.Attachments) { if (!File.Exists(attachment)) { _logger.Warning(附件不存在: {Attachment}, attachment); continue; } message.Attachments.Add(new Attachment(attachment)); } try { await _client.SendMailAsync(message); _logger.Information(发送成功: {Subject}, request.Subject); } catch (Exception ex) { _logger.Error(ex, 发送失败); throw; } } }在实际项目中我通常会进一步封装添加DI容器注册扩展实现IMailService接口配置Polly重试策略集成配置热更新这个实现已经成功应用于日均发送量10万的电商系统通过合理的队列管理和连接池配置即使在促销高峰期也能保持稳定运行。对于需要更高可靠性的场景建议结合消息队列如RabbitMQ实现分布式邮件服务。