SmtpClient默认发不出去,因Gmail等强制TLS/SSL且校验发件人、附件名编码、大小限制;需设EnableSsl=true、Credentials为完整邮箱+授权码、附件用ContentType.Name指定UTF8编码、发送前校验文件大小。

直接用 SmtpClient 发带附件的邮件是可行的,但默认配置下极大概率发不出去——不是认证失败,就是被当成垃圾邮件,或者附件乱码、超限、丢失。关键不在“会不会写”,而在“怎么配才真正可用”。
为什么 SmtpClient 默认发不出去
现代邮箱(Gmail、Outlook、QQ 邮箱等)基本禁用了明文 SMTP 认证,强制要求 TLS/SSL;同时对发信频率、发件人域名、HELO 声明、附件类型都有校验。直接 new 一个 SmtpClient 并填入账号密码,十有八九会抛出 AuthenticationFailedException 或 SmtpException(错误信息类似 “The server response was: 5.7.0 Authentication Required”)。
必须显式设置以下几点:
-
EnableSsl = true(注意:不是所有端口都支持,Port = 587对应EnableSsl = true,而Port = 465通常需设为UseDefaultCredentials = false+ 手动启用 SSL) -
Credentials必须是NetworkCredential,且用户名通常是完整邮箱地址(如"user@gmail.com"),不是昵称或 ID -
DeliveryMethod = SmtpDeliveryMethod.Network(避免误用 PickupDirectory) - 发件人地址(
MailMessage.From)必须与认证账号一致,否则多数服务商拒绝投递
附件添加的三个易错点
附件看似只是调用 Attachments.Add(),但实际踩坑密集:
- 路径必须是绝对路径,相对路径在 IIS 或 Windows Service 下极易找不到文件,建议用
Path.GetFullPath()校验 - 附件名含中文时,必须设置
ContentType.Name并指定编码,否则 Outlook 显示乱码:var attachment = new Attachment(filePath); attachment.NameEncoding = Encoding.UTF8; attachment.ContentType = new ContentType("application/octet-stream") { Name = "报告.pdf" }; - 单个附件超过 25MB(Gmail)、50MB(Outlook)会静默失败,
SmtpClient.Send()可能不抛异常但收件方收不到——务必在发送前用new FileInfo(filePath).Length检查大小并提示用户
推荐封装成可复用的工具类(.NET 6+)
不要每次发邮件都重复写一堆配置。下面这个轻量工具类覆盖了主流场景,且把敏感配置外移:
public class EmailSender
{
private readonly string _host;
private readonly int _port;
private readonly string _userName;
private readonly string _password;
<pre class="brush:php;toolbar:false;">public EmailSender(string host, int port, string userName, string password)
{
_host = host;
_port = port;
_userName = userName;
_password = password;
}
public bool Send(string to, string subject, string body, List<string> attachmentPaths = null)
{
try
{
var msg = new MailMessage(_userName, to, subject, body)
{
IsBodyHtml = true,
BodyEncoding = Encoding.UTF8,
SubjectEncoding = Encoding.UTF8
};
if (attachmentPaths?.Count > 0)
{
foreach (var path in attachmentPaths)
{
if (!File.Exists(path)) continue;
var att = new Attachment(path);
att.NameEncoding = Encoding.UTF8;
msg.Attachments.Add(att);
}
}
using var client = new SmtpClient(_host, _port)
{
EnableSsl = _port == 587 || _port == 465,
Credentials = new NetworkCredential(_userName, _password)
};
client.Send(msg);
return true;
}
catch (Exception ex) when (ex is SmtpException or IOException)
{
// 记录 ex.ToString(),尤其关注 InnerException.Message
return false;
}
}}
使用示例:
var sender = new EmailSender("smtp.gmail.com", 587, "me@gmail.com", "app-specific-password");
sender.Send("to@example.com", "测试", "<p>正文</p>", new[] { @"C:\tmp\file.pdf" }.ToList());注意:Gmail 必须用「应用专用密码」而非账户密码。
调试时最该看的三处日志
发不出邮件时,别急着改代码。先确认这三项:
- SMTP 服务器返回的原始响应:捕获
SmtpException的StatusCode和InnerException.Message,例如SmtpStatusCode.MustIssueStartTlsFirst表示没开 TLS - Windows 事件查看器 → 应用程序日志里是否有
System.Net.Mail相关警告(需开启system.net跟踪,见 Microsoft 文档) - 用 Wireshark 抓包看 TCP 三次握手是否成功、TLS 握手是否完成——很多公司内网会拦截 587 端口
附件名乱码、发件人被拒、连接超时……这些问题背后往往不是 C# 写错了,而是 SMTP 服务端策略和本地网络环境共同作用的结果。配对、验证、日志,比堆代码更重要。


















