发不出带附件的邮件主因是SmtpClient加密配置错误、流管理不当或中文名未按RFC 2231编码;必须显式设EnableSsl=true并配587端口,附件流需手动控制生命周期,中文名须设ContentDisposition.FileName,大附件应改用云链接。

发不出带附件的邮件,90% 不是代码写错,而是 SmtpClient 没配对加密、流没管好、或中文名没走 RFC 2231 编码路径。
SmtpClient.EnableSsl 和端口必须同时设对
多数人卡在连接被拒,错误信息常是“无法建立安全连接”或直接超时。Gmail、Outlook、QQ、163 邮箱全部强制 TLS(STARTTLS),不是可选项。
-
EnableSsl = true必须显式设为true,不能留默认值或靠注释跳过 - 端口必须匹配:统一用
587(STARTTLS),别用465—— .NET Core 3.0+ 已弃用465的 SSL 封装,连上也会协商失败 - 企业内网 SMTP 若不需加密,得先
telnet smtp.internal.com 25确认通路,再设EnableSsl = false并用25端口
Attachment 构造时流必须由你控制生命周期
传 new Attachment("path.pdf") 看似省事,但内部 FileStream 关闭时机不可控;循环发多封邮件时极易触发“文件正由另一进程使用”。
- 正确做法:自己开流,包在
using里,且确保流在SmtpClient.Send()返回前仍打开 - 示例:
using (var stream = File.OpenRead("report.pdf")) { var attachment = new Attachment(stream, "report.pdf", "application/pdf"); mailMessage.Attachments.Add(attachment); smtpClient.Send(mailMessage); // stream 在 Send 返回后才 Dispose } - 若附件来自内存(如 PDF 字节数组),用
new MemoryStream(data),且别忘stream.Position = 0
中文附件名乱码?设 ContentDisposition.FileName 而不是 Name
直接赋 attachment.Name = "测试.pdf" 会变成问号或下划线——因为 SMTP 头部要求 RFC 2231 编码,而 Name 属性不自动编码。
- 必须走
ContentDisposition:设置attachment.ContentDisposition.FileName = "测试.pdf" - 这会自动生成
filename*=UTF-8''%E6%B5%8B%E8%AF%95.pdf头部,Gmail/Outlook 支持,老旧网关可能丢名 - 别同时设
attachment.Name和ContentDisposition.FileName,后者优先,前者会被忽略
大附件(>10MB)失败别怪代码,先查服务商限制
SMTP 协议本身不限大小,但现实是 Gmail/Outlook/QQ 都硬性限制单封邮件总大小 ≤25MB;Base64 编码后附件实际可用约 20–22MB。
- 超限典型错误:
SmtpException,消息含552 5.3.4 Message size exceeds fixed maximum message size -
SmtpClient.Timeout默认 100,000ms(100 秒),上传 20MB 在普通网络下大概率超时,建议调到300000 - 真要发大文件,别硬扛:生成临时云链接(如 OneDrive/腾讯微云),正文里放下载地址更可靠
最易被忽略的是:.NET 6+ 中 SmtpClient 已标记为 Obsolete,它还能用,但未来某天会删;现在就该评估迁移到 MailKit,尤其当你需要 OAuth2、异步发送或细粒度错误诊断时。


















