C#基于SMTP如何实现每日指定时间自动发送带附件邮件
.NET平台无依赖实现工作日15点自动发邮件方案
不要直接写DateTime.Now == 下午3点的判断——这种精确到毫秒的相等判断,受轮询间隔、系统时间调度偏差影响,90%概率会错过触发点,而且写死循环轮询时间会平白占用CPU资源,用内置的异步延时逻辑就能零额外依赖实现需求。
核心定时逻辑
先写方法计算距离下一个触发时间的间隔,自动跳过非工作日、自动处理过点顺延的情况,不用手写周一到周五的重复判断:
DateTime GetNextTriggerTime() { DateTime now = DateTime.Now; DateTime target = now.Date.AddHours(15); // 当日15点整 // 当日15点已过,顺延到下一天 if (now > target) target = target.AddDays(1); // 自动跳过周末,只保留工作日触发 while (target.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday) { target = target.AddDays(1); } return target; }
然后写常驻的调度逻辑,用Task.Delay做非阻塞等待,等待期间几乎不消耗系统资源,比Thread.Sleep或者空循环轮询效率高很多:
async Task ScheduleRunner() { while (true) { DateTime nextSend = GetNextTriggerTime(); TimeSpan waitSpan = nextSend - DateTime.Now; // 等待到触发点 await Task.Delay(waitSpan); // 执行发邮件逻辑 await SendWorkEmail(); // 发完等60秒,避免同一时间点重复触发 await Task.Delay(TimeSpan.FromMinutes(1)); } }
邮件发送逻辑优化
你原有SMTP代码逻辑基本可用,调整下资源释放、附件匹配的逻辑,不用复制5段星期判断,用字典映射星期和对应附件路径即可:
async Task SendWorkEmail() { try { // 配置各工作日对应附件路径,改路径直接改这里就行 var weekAttachmentMap = new Dictionary<DayOfWeek, string> { [DayOfWeek.Monday] = @"C:\local\周一报表.xlsx", [DayOfWeek.Tuesday] = @"C:\local\周二报表.xlsx", [DayOfWeek.Wednesday] = @"C:\local\周三报表.xlsx", [DayOfWeek.Thursday] = @"C:\local\周四报表.xlsx", [DayOfWeek.Friday] = @"C:\local\周五报表.xlsx" }; using var client = new SmtpClient("你的SMTP服务地址", 端口) { EnableSsl = false, DeliveryMethod = SmtpDeliveryMethod.Network, UseDefaultCredentials = false, Credentials = new NetworkCredential("发件邮箱账号", "发件邮箱密码") }; using var msg = new MailMessage { IsBodyHtml = true, From = new MailAddress("你的发件邮箱地址"), Subject = "定时发送的工作邮件", Body = "你的HTML格式邮件正文" }; msg.To.Add("收件人邮箱地址"); // 自动匹配当天附件添加 if (weekAttachmentMap.TryGetValue(DateTime.Now.DayOfWeek, out var attachPath)) { msg.Attachments.Add(new Attachment(attachPath)); } await client.SendMailAsync(msg); Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 邮件发送成功"); } catch (Exception ex) { Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 发送失败:{ex.Message}"); } }
启动方式
如果是控制台程序,直接在入口函数调用调度方法即可:
// 程序启动后直接进入调度等待 await ScheduleRunner();
注意事项
- 不需要额外安装数据库、第三方定时任务组件,所有逻辑都是.NET内置API实现
- 开发调试阶段可以保持VS开着运行,正式用的时候编译成Release版本的exe直接双击后台运行即可,不需要一直开VS
- 不要用空循环+时间相等判断的写法,既耗性能又容易漏触发,提前算等待时长的方式时间精度可以到毫秒级,完全满足准点发送需求
- 如果需要临时调整发送时间,只需要改
AddHours(15)里的小时数值就行,其他逻辑不用动
内容的提问来源于stack exchange,提问作者user12422846
相关产品推荐
相关产品推荐

