.NET Core 3.0+ 中 SmtpClient 已过时,推荐用 MailKit;附件需用 Attachment 添加且流不能提前关闭;中文名需设 ContentDisposition.FileName 触发 RFC 2231 编码;大附件应检查大小并考虑云链接方案。

使用 SmtpClient 和 MailMessage 发送带附件的邮件
直接用 System.Net.Mail 是最常见做法,但要注意:.NET Core 3.0+ 和 .NET 5+ 中 SmtpClient 已标记为过时(obsolete),官方推荐改用第三方库(如 MailKit),不过它仍能正常工作。关键在于附件必须通过 Attachment 实例添加,且不能在发送后立即释放流。
常见错误是传入已关闭的文件流或使用相对路径导致找不到文件:
-
Attachment构造函数若接收Stream,该流在MailMessage.Dispose()前必须保持打开状态;否则发信失败或附件为空 - 使用
new Attachment("path/to/file.pdf")更安全,框架内部会自动打开/关闭文件流 - 路径建议用
Path.GetFullPath()转成绝对路径,避免因工作目录不同而报FileNotFoundException
附件中文名乱码或无法识别的问题
默认情况下,Attachment 的 ContentType 不含 name* 参数,部分邮箱(如 Outlook、Apple Mail)会把中文附件名显示为 noname 或乱码。
解决方法是手动设置 ContentDisposition:
var attachment = new Attachment("report.xlsx");
attachment.ContentDisposition.FileName = "报表.xlsx"; // 触发 RFC 2231 编码
attachment.ContentDisposition.DispositionType = "attachment";
message.Attachments.Add(attachment);
注意:FileName 属性赋值会自动启用 UTF-8 编码(name*=UTF-8''...),但前提是 SMTP 服务器不强制 strip 头部——Gmail、Outlook.com 通常支持,老旧企业邮件网关可能不兼容。
大附件(>10MB)发送失败或超时
不是代码问题,而是现实约束:多数公共 SMTP 服务(如 Gmail、QQ 邮箱)限制单封邮件总大小在 25MB 内,附件实际可用约 20–22MB(Base64 编码膨胀约 33%)。超过即触发 SmtpException,错误信息类似 552 5.3.4 Message size exceeds fixed maximum message size。
应对策略:
- 发送前用
new FileInfo(path).Length检查附件大小,提前提示用户 - 多个小文件可打包为 ZIP 并设密码(避免被邮件系统拦截),但需确保收件人能解压
- 真正的大文件应改用「邮件+云链接」模式:上传到 OneDrive / 阿里云 OSS 后,邮件正文只放下载链接
.NET 6+ 推荐替代方案:用 MailKit 替代 SmtpClient
SmtpClient 的过时警告不是吓唬人——它不支持现代认证方式(如 OAuth2),且 SSL/TLS 行为难以细粒度控制。MailKit 是目前最成熟的跨平台替代,API 清晰,对附件、编码、超时控制更可靠。
最小可行示例:
using var client = new MailKit.Net.Smtp.SmtpClient();
client.Connect("smtp.gmail.com", 587, MailKit.Security.SecureSocketOptions.StartTls);
client.Authenticate("user@gmail.com", "app-password"); // 注意:Gmail 需用 App Password
var message = new MimeMessage();
message.To.Add(new MailboxAddress("收件人", "to@example.com"));
message.Subject = "带附件测试";
var builder = new BodyBuilder();
builder.TextBody = "正文内容";
builder.Attachments.Add("document.pdf"); // 自动处理编码和流生命周期
message.Body = builder.ToMessageBody();
client.Send(message);
重点:MailKit 的 Attachments.Add() 支持路径、字节数组、Stream,内部统一管理资源释放,不会出现“附件为空”这类低级故障。
真正麻烦的从来不是怎么加附件,而是附件从哪来、有多大、要不要加密、收件人能不能下——这些不在代码里,但在上线前必须问清楚。










