1. 为什么需要自己实现邮件发送功能?
在C#开发中,邮件发送是一个看似简单但实际暗藏玄机的功能。虽然.NET框架已经提供了System.Net.Mail命名空间下的基础邮件发送类,但真正要在生产环境中实现一个健壮的邮件发送功能,需要考虑的因素远比想象中多。
我曾在多个项目中遇到过这样的场景:开发人员直接使用MailMessage和SmtpClient发送邮件,结果在测试环境一切正常,到了生产环境却频繁出现邮件丢失、附件损坏、编码混乱等问题。更糟糕的是,当邮件发送量增大时,同步发送方式会导致应用程序响应变慢,严重影响用户体验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 邮件发送核心组件解析
2.1 System.Net.Mail命名空间
.NET框架提供的邮件发送功能主要包含以下几个核心类:
- MailMessage:表示电子邮件消息
- SmtpClient:用于将电子邮件发送到SMTP服务器
- Attachment:表示电子邮件的附件
- AlternateView:提供电子邮件的替代视图
这些类虽然基础,但已经能够满足大多数邮件发送需求。下面是一个最简单的邮件发送示例:
csharp复制using System.Net;
using System.Net.Mail;
var fromAddress = new MailAddress("sender@example.com", "Sender Name");
var toAddress = new MailAddress("recipient@example.com", "Recipient Name");
const string fromPassword = "password";
const string subject = "Test Email";
const string body = "This is a test email";
var smtp = new SmtpClient
{
Host = "smtp.example.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
2.2 邮件发送的常见问题
在实际开发中,我发现以下几个问题特别常见:
- 编码问题:特别是当邮件内容包含多国语言时,容易出现乱码
- 附件问题:大附件发送失败、附件损坏、附件名称乱码
- 超时问题:SMTP服务器响应慢导致发送超时
- 认证问题:不同邮件服务商的认证方式差异
- 垃圾邮件过滤:邮件内容被误判为垃圾邮件
3. 实现支持附件的邮件发送功能
3.1 基础邮件发送实现
让我们先实现一个基础的邮件发送方法,支持文本邮件和HTML邮件:
csharp复制public class EmailService
{
private readonly SmtpClient _smtpClient;
private readonly string _fromAddress;
public EmailService(string host, int port, bool enableSsl,
string userName, string password, string fromAddress)
{
_smtpClient = new SmtpClient
{
Host = host,
Port = port,
EnableSsl = enableSsl,
Credentials = new NetworkCredential(userName, password)
};
_fromAddress = fromAddress;
}
public async Task SendEmailAsync(string to, string subject, string body, bool isBodyHtml = false)
{
var mailMessage = new MailMessage
{
From = new MailAddress(_fromAddress),
Subject = subject,
Body = body,
IsBodyHtml = isBodyHtml
};
mailMessage.To.Add(to);
await _smtpClient.SendMailAsync(mailMessage);
}
}
3.2 添加附件支持
附件处理是邮件发送中最容易出问题的部分之一。我们需要特别注意以下几点:
- 附件大小限制
- 附件名称编码
- 附件内容类型
- 多个附件处理
下面是增强版的邮件发送方法,支持附件:
csharp复制public async Task SendEmailWithAttachmentsAsync(string to, string subject, string body,
bool isBodyHtml = false, List<EmailAttachment> attachments = null)
{
var mailMessage = new MailMessage
{
From = new MailAddress(_fromAddress),
Subject = subject,
Body = body,
IsBodyHtml = isBodyHtml
};
mailMessage.To.Add(to);
if (attachments != null && attachments.Any())
{
foreach (var attachment in attachments)
{
var memoryStream = new MemoryStream(attachment.Content);
var mailAttachment = new Attachment(memoryStream, attachment.FileName, attachment.MediaType);
// 处理附件名称编码
mailAttachment.NameEncoding = Encoding.UTF8;
mailMessage.Attachments.Add(mailAttachment);
}
}
await _smtpClient.SendMailAsync(mailMessage);
// 释放附件资源
foreach (var attachment in mailMessage.Attachments)
{
attachment.Dispose();
}
}
public class EmailAttachment
{
public string FileName { get; set; }
public byte[] Content { get; set; }
public string MediaType { get; set; }
}
3.3 处理大附件
当附件较大时(通常超过10MB),直接使用MemoryStream可能会导致内存问题。这时我们可以改用文件流:
csharp复制public async Task SendLargeAttachmentAsync(string to, string subject, string body,
string attachmentPath)
{
var mailMessage = new MailMessage
{
From = new MailAddress(_fromAddress),
Subject = subject,
Body = body
};
mailMessage.To.Add(to);
var attachment = new Attachment(attachmentPath);
mailMessage.Attachments.Add(attachment);
try
{
await _smtpClient.SendMailAsync(mailMessage);
}
finally
{
attachment.Dispose();
}
}
4. 高级邮件发送功能实现
4.1 支持抄送和密送
在实际业务中,我们经常需要抄送或密送邮件给相关人员:
csharp复制public async Task SendEmailWithCopiesAsync(string to, string subject, string body,
List<string> cc = null, List<string> bcc = null)
{
var mailMessage = new MailMessage
{
From = new MailAddress(_fromAddress),
Subject = subject,
Body = body
};
mailMessage.To.Add(to);
if (cc != null)
{
foreach (var ccAddress in cc)
{
mailMessage.CC.Add(ccAddress);
}
}
if (bcc != null)
{
foreach (var bccAddress in bcc)
{
mailMessage.Bcc.Add(bccAddress);
}
}
await _smtpClient.SendMailAsync(mailMessage);
}
4.2 支持HTML模板邮件
现代邮件通常使用HTML格式,我们可以使用模板引擎来生成漂亮的HTML邮件:
csharp复制public async Task SendHtmlEmailAsync(string to, string subject, string templatePath,
object model)
{
var template = await File.ReadAllTextAsync(templatePath);
var body = RenderTemplate(template, model);
await SendEmailAsync(to, subject, body, true);
}
private string RenderTemplate(string template, object model)
{
// 这里可以使用RazorEngine或其他模板引擎
// 简化示例,实际项目中应该使用成熟的模板引擎
foreach (var property in model.GetType().GetProperties())
{
template = template.Replace($"{{{{{property.Name}}}}}",
property.GetValue(model)?.ToString());
}
return template;
}
4.3 邮件发送队列
在高并发场景下,直接发送邮件可能会导致性能问题。我们可以实现一个简单的邮件发送队列:
csharp复制publ
