用SmtpClient发附件必须设EnableSsl=true并匹配端口(如Gmail/QQ邮箱用587),附件需用using管理流,中文名须通过ContentType.Name设置以自动RFC2231编码,大附件需调Timeout并检查服务商大小限制。

用 SmtpClient 发附件前必须设 EnableSsl 和端口
多数人卡在发不出邮件,不是代码写错,而是 SMTP 服务器拒绝未加密连接。Gmail、Outlook、QQ 邮箱都强制要求 TLS/SSL,SmtpClient 默认不启用加密,必须显式设置。
-
EnableSsl = true是必须项,不能靠猜测或留空 - 端口号得匹配加密方式:Gmail 用
587(STARTTLS),不是465(旧 SSL);QQ 邮箱同理,465在 .NET Core 3.0+ 已被弃用,推荐统一用587+EnableSsl = true - 如果用企业自建 SMTP,先 telnet 测试端口连通性,再确认是否要
EnableSsl—— 有些内网 SMTP 根本不要加密
Attachment 对象必须用 using 或手动 Dispose
附件文件流没释放会导致“文件正由另一进程使用”错误,尤其在循环发多封带附件的邮件时,Attachment 内部持有的 FileStream 不会自动关掉。
- 别这么写:
new Attachment("report.pdf")—— 它内部用默认构造打开文件,但没暴露流供你控制 - 正确做法是自己开流,传入
Attachment构造函数,并确保Dispose():using (var stream = File.OpenRead("report.pdf")) { var attachment = new Attachment(stream, "report.pdf", "application/pdf"); mailMessage.Attachments.Add(attachment); } - 如果附件来自内存(比如生成的 PDF 字节数组),用
new MemoryStream(data)构造流,同样要包在using里
中文附件名乱码?用 System.Net.Mime.ContentType 设 Name 属性
直接赋值 attachment.Name = "测试报告.pdf" 会变成问号或下划线,因为 SMTP 协议要求附件名用 RFC 2231 编码,而 Attachment 的 Name 属性不自动编码。
- 解决方法是改用
ContentType的Name属性:var attachment = new Attachment(stream, "dummy.pdf"); attachment.ContentType.Name = "测试报告.pdf"; // 这个 Name 会自动编码
- 注意:不能同时设
attachment.Name和attachment.ContentType.Name,后者优先,前者会被忽略 - 如果收件方是老式邮件客户端(比如 Outlook 2003),建议附件名只用 ASCII 字符,避免兼容性问题
大附件(>10MB)容易超时或被拒,得调 Timeout 和检查 SMTP 限制
默认 SmtpClient.Timeout 是 100,000 毫秒(100 秒),上传 20MB 文件在普通网络下大概率超时;更麻烦的是,多数公共 SMTP 服务(Gmail/Outlook/QQ)本身限制单封邮件总大小为 25MB,附件一多就直接 552 错误。
- 增大超时:
smtpClient.Timeout = 300_000;(5 分钟),但别设无限 —— 网络卡死会一直挂住线程 - 发前先检查总大小:
mailMessage.Attachments.Cast<attachment>().Sum(a => a.ContentStream.Length)</attachment>,加上正文估算,别超服务商上限 - 真要传大文件,别硬塞邮件:生成下载链接 + 临时 token,邮件里只放 URL
附件路径不存在、流已关闭、SMTP 认证失败这三类错误最常被吞成泛化异常,调试时记得 catch SmtpException 并打 StatusCode 和 ErrorMessage,别只看 ToString()。


















