NET6/Framework 封装邮件发送纯文本/HTML/HTML+图片/附件
Nuget
Runtime: Net6 MailKit 4.2.0 MimeKit 4.2.0
发送纯文本
{ var bodyBuilder = new BodyBuilder(); bodyBuilder.TextBody = "这是一封纯文本邮件"; message.Body = bodyBuilder.ToMessageBody(); } { var textPart = new TextPart("plain") { Text = "这是一封纯文本邮件" }; message.Body = textPart; }
发送HTML
{ var bodyBuilder = new BodyBuilder(); bodyBuilder.HtmlBody = "<html><body><h1>这是一封包含HTML的邮件</h1></body></html>"; message.Body = bodyBuilder.ToMessageBody(); } { var multipart = new Multipart("mixed"); var htmlPart = new TextPart("html") { Text = "<html><body><h1>这是一封包含HTML的邮件</h1></body></html>" }; multipart.Add(htmlPart); message.Body = multipart; }
HTML+图片
var contentId = MimeUtils.GenerateMessageId(); var str = "<html><body><h1>这是一封HTML包含图片的邮件</h1><img src=\"cid:"+ contentId +"\"></body></html>"; { var bodyBuilder = new BodyBuilder(); var image = bodyBuilder.LinkedResources.Add("图片路径"); image.ContentId = contentId; bodyBuilder.HtmlBody = str; message.Body = bodyBuilder.ToMessageBody(); } { var multipart = new Multipart("related"); var htmlPart = new TextPart("html") { Text = str }; multipart.Add(htmlPart); var image = new MimePart("image", "jpeg") { Content = new MimeContent(File.OpenRead("图片路径")), ContentDisposition = new ContentDisposition(ContentDisposition.Inline), ContentTransferEncoding = ContentEncoding.Base64, ContentId = contentId }; multipart.Add(image); message.Body = multipart; }
附件
{ var attachment = new MimePart("application", "octet-stream") { Content = new MimeContent(File.OpenRead("附件路径")), ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), ContentTransferEncoding = ContentEncoding.Base64, FileName = Path.GetFileName("附件路径") }; bodyBuilder.Attachments.Add(attachment); } { var multipart = new Multipart("mixed"); var attachment = new MimePart("application", "附件后缀") { Content = new MimeContent(File.OpenRead("附件路径")), ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), ContentTransferEncoding = ContentEncoding.Base64, FileName = Path.GetFileName("附件路径") }; multipart.Add(attachment); message.Body = multipart; }
Demo
Models
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 自定义配置文件对象 3 /// </summary> 4 public class AppSettingDto 5 { 6 /// <summary> 7 /// 邮件配置信息 8 /// </summary> 9 public EmailInfo EmailInfo { get; set; } 10 } 11 12 /// <summary> 13 /// 邮件配置信息 14 /// </summary> 15 public class EmailInfo 16 { 17 /// <summary> 18 /// 邮件:主机 19 /// </summary> 20 public string EmailHost { get; set; } 21 22 /// <summary> 23 /// 邮件:端口 24 /// </summary> 25 public int EmailPort { get; set; } 26 27 /// <summary> 28 /// 发送人账号 29 /// </summary> 30 public string EmailFromAccount { get; set; } 31 32 /// <summary> 33 /// 发送人密码 34 /// </summary> 35 public string EmailFromPassword { get; set; } 36 37 /// <summary> 38 /// 发送人邮件地址 39 /// </summary> 40 public string EmailFromAddress { get; set; } 41 } 42 43 /// <summary> 44 /// 邮件用户 45 /// </summary> 46 public class EmailTargetDto 47 { 48 /// <summary> 49 /// 邮件账号 50 /// </summary> 51 public string EmailAccount { get; set; } 52 53 /// <summary> 54 /// 邮件地址 55 /// </summary> 56 public string EmailAddress { get; set; } 57 } 58 59 /// <summary> 60 /// 邮件内容类型枚举:纯文本 / 页面:文本 / 页面:项目内相对路径 61 /// </summary> 62 public enum EmailTypeEnum 63 { 64 /// <summary> 65 /// 纯文本 66 /// </summary> 67 Text = 1, 68 69 /// <summary> 70 /// 页面:文本 71 /// </summary> 72 HtmlText = 10, 73 74 /// <summary> 75 /// 页面:项目内相对路径 76 /// </summary> 77 HtmlPath = 13, 78 } 79 80 /// <summary> 81 /// 邮件参数dto 82 /// </summary> 83 public class SendEmailDto 84 { 85 /// <summary> 86 /// 主题 87 /// </summary> 88 public string Subject { get; set; } 89 90 /// <summary> 91 /// 邮件集合:接收人 92 /// </summary> 93 public List<EmailTargetDto> ToList { get; set; } = new List<EmailTargetDto>(); 94 95 /// <summary> 96 /// 邮件集合:抄送人 97 /// </summary> 98 public List<EmailTargetDto> CcList { get; set; } = new List<EmailTargetDto>(); 99 100 /// <summary> 101 /// 邮件内容类型枚举:纯文本 / 页面:文本 / 页面:项目内相对路径 102 /// </summary> 103 public EmailTypeEnum Type { get; set; } 104 105 /// <summary> 106 /// 邮件内容 107 /// </summary> 108 public string Content { get; set; } 109 110 /// <summary> 111 /// 页面:项目内相对路径(ContentType == EmailTypeEnum.HtmlPath) 112 /// </summary> 113 public string HtmlPath { get; set; } 114 115 /// <summary> 116 /// html内图片路径集合:项目内的相对路径(路径必须和占位符保持一致,且,占位符数量要小于等于路径数量) 117 /// </summary> 118 public List<string> HtmlImgPathList { get; set; } 119 120 /// <summary> 121 /// 附件地址集合:项目内的相对路径 122 /// </summary> 123 public List<string> AttachmentPathList { get; set; } 124 }
Helper
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 邮件帮助类:仅支持纯文本,html,附件 3 /// </summary> 4 public class EmailHelper 5 { 6 /// <summary> 7 /// 邮件配置信息 8 /// </summary> 9 private EmailInfo _email; 10 11 public EmailHelper(IOptionsSnapshot<AppSettingDto> options) 12 { 13 _email = options.Value.EmailInfo; 14 } 15 16 /// <summary> 17 /// 发送文本 18 /// </summary> 19 /// <param name="content"></param> 20 public void SendText(SendEmailDto dto) 21 { 22 dto.Type = EmailTypeEnum.Text; 23 SendRun(dto); 24 } 25 26 /// <summary> 27 /// 发送Html文本 28 /// </summary> 29 /// <param name="content"></param> 30 public void SendHtmlText(SendEmailDto dto) 31 { 32 dto.Type = EmailTypeEnum.HtmlText; 33 SendRun(dto); 34 } 35 36 /// <summary> 37 /// 发送Html相对路径 38 /// </summary> 39 /// <param name="content"></param> 40 public void SendHtmlPath(SendEmailDto dto) 41 { 42 dto.Type = EmailTypeEnum.HtmlPath; 43 SendRun(dto); 44 } 45 46 /// <summary> 47 /// 发送邮件 48 /// </summary> 49 /// <param name="dto"></param> 50 private void SendRun(SendEmailDto dto) 51 { 52 // 构建邮件:MimeMessage 53 var message = BuilderEmailMessage(dto); 54 // 构建邮件:Body 55 var bodyBuilder = BuilderEmailBody(dto, message); 56 // 绑定附件 57 BindingAttachment(dto, bodyBuilder); 58 message.Body = bodyBuilder.ToMessageBody(); 59 // 发送邮件 60 SendMessage(message); 61 } 62 63 /// <summary> 64 /// 绑定附件 65 /// </summary> 66 /// <param name="dto"></param> 67 /// <param name="bodyBuilder"></param> 68 private static void BindingAttachment(SendEmailDto dto, BodyBuilder bodyBuilder) 69 { 70 var attachmentList = dto?.AttachmentPathList ?? new List<string>(); 71 var baseDirctory = AppDomain.CurrentDomain.BaseDirectory; 72 foreach (var itemPath in attachmentList) 73 { 74 var attachment = new MimePart("application", "octet-stream") 75 { 76 Content = new MimeContent(File.OpenRead($@"{baseDirctory}\{itemPath}")), 77 ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), 78 ContentTransferEncoding = ContentEncoding.Base64, 79 FileName = Path.GetFileName(itemPath) 80 }; 81 bodyBuilder.Attachments.Add(attachment); 82 } 83 } 84 85 /// <summary> 86 /// 构建邮件:MimeMessage 87 /// </summary> 88 /// <param name="dto"></param> 89 /// <returns></returns> 90 private MimeMessage BuilderEmailMessage(SendEmailDto dto) 91 { 92 var message = new MimeMessage 93 { 94 Subject = dto.Subject, 95 }; 96 message.From.Add(new MailboxAddress(_email.EmailFromAccount, _email.EmailFromAddress)); 97 if (dto.ToList != null && dto.ToList.Count > 0) dto.ToList.ForEach(x => message.To.Add(new MailboxAddress(x.EmailAccount, x.EmailAddress))); 98 if (dto.CcList != null && dto.CcList.Count > 0) dto.CcList.ForEach(x => message.Cc.Add(new MailboxAddress(x.EmailAccount, x.EmailAddress))); 99 return message; 100 } 101 102 /// <summary> 103 /// 构建邮件:Body 104 /// </summary> 105 /// <param name="dto"></param> 106 /// <param name="message"></param> 107 private BodyBuilder BuilderEmailBody(SendEmailDto dto, MimeMessage message) 108 { 109 var bodyBuilder = new BodyBuilder(); 110 switch (dto.Type) 111 { 112 case EmailTypeEnum.HtmlText: 113 case EmailTypeEnum.HtmlPath: 114 // 把 html相对路径 处理成 html文本 115 var htmlValue = HandleHtmlText(dto); 116 // 把 html文本 中的img 转换为 base64 117 htmlValue = HandleHtmlImg(bodyBuilder, htmlValue, dto.HtmlImgPathList); 118 bodyBuilder.HtmlBody = htmlValue; 119 break; 120 case EmailTypeEnum.Text: 121 default: 122 bodyBuilder.TextBody = dto?.Content?.Trim(); ; 123 break; 124 } 125 return bodyBuilder; 126 } 127 128 /// <summary> 129 /// 把 html文本 中的img 转换为 base64 130 /// </summary> 131 /// <param name="dto"></param> 132 /// <returns></returns> 133 private string HandleHtmlImg(BodyBuilder bodyBuilder, string htmlValue, List<string> htmlImgPathList) 134 { 135 if (htmlImgPathList == null || htmlImgPathList.Count == 0) return htmlValue; 136 var baseDirctory = AppDomain.CurrentDomain.BaseDirectory; 137 // 循环处理img to base64 138 var imgPathList = new List<string>(); 139 foreach (var itemPath in htmlImgPathList) 140 { 141 var image = bodyBuilder.LinkedResources.Add($@"{baseDirctory}\{itemPath}"); 142 image.ContentId = MimeUtils.GenerateMessageId(); 143 string imgPath = $"cid:{image.ContentId}"; 144 imgPathList.Add(imgPath); 145 } 146 147 // 路径必须和占位符保持一致,且,占位符数量要小于等于路径数量 148 return string.Format(htmlValue, imgPathList.ToArray()); 149 } 150 151 /// <summary> 152 /// 把 html相对路径 处理成 html文本 153 /// </summary> 154 /// <param name="dto"></param> 155 /// <returns></returns> 156 private string HandleHtmlText(SendEmailDto dto) 157 { 158 // 页面:项目内相对路径 159 if (dto.Type == EmailTypeEnum.HtmlPath) 160 { 161 var baseDirctory = AppDomain.CurrentDomain.BaseDirectory; 162 var htmlPath = $@"{baseDirctory}\{dto.HtmlPath}"; 163 using (StreamReader SourceReader = File.OpenText(htmlPath)) 164 { 165 var htmlValue = SourceReader.ReadToEnd(); 166 return htmlValue; 167 } 168 } 169 return dto.Content; 170 } 171 172 /// <summary> 173 /// 发送邮件 174 /// </summary> 175 /// <param name="message"></param> 176 private void SendMessage(MimeMessage message) 177 { 178 using (var client = new SmtpClient()) 179 { 180 // 地址端口 181 client.Connect(_email.EmailHost, _email.EmailPort); 182 // 账号密码 183 client.Authenticate(_email.EmailFromAccount, _email.EmailFromPassword); 184 185 // 发送邮件 186 client.Send(message); 187 client.Disconnect(true); 188 } 189 message.Dispose(); 190 } 191 }
Run
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 单元测试 3 /// </summary> 4 public class UnitTest 5 { 6 private EmailInfo _email; 7 private EmailHelper _emailUnit; 8 9 /// <summary> 10 /// 11 /// </summary> 12 /// <param name="emailUnit"></param> 13 /// <param name="email"></param> 14 public UnitTest(EmailHelper emailUnit, IOptionsSnapshot<AppSettingDto> options) 15 { 16 _emailUnit = emailUnit; 17 _email = options.Value.EmailInfo; 18 } 19 20 /// <summary> 21 /// 发送文本 22 /// </summary> 23 [Fact] 24 public void SendTextDto() 25 { 26 var dto = new SendEmailDto 27 { 28 Subject = $"测试Net6邮件[Text] - {DateTime.Now:yyyy-MM-dd HH:mm:ss}", 29 ToList = new List<EmailTargetDto> 30 { 31 new EmailTargetDto 32 { 33 EmailAccount = _email.EmailFromAccount, 34 EmailAddress = _email.EmailFromAddress, 35 } 36 }, 37 Content = "这是一封测试邮件" 38 }; 39 _emailUnit.SendText(dto); 40 } 41 42 /// <summary> 43 /// 发送Html文本 44 /// </summary> 45 [Fact] 46 public void SendHtmlTextDto() 47 { 48 var builder = new StringBuilder(); 49 builder.AppendLine("<html>"); 50 builder.AppendLine(" <head></head>"); 51 builder.AppendLine(" <body style='text-align: center; width: 630px !important; height:100px !important; border: 1px solid red;'>"); 52 builder.AppendLine(" <div style='width: 630px; height: 22px;'>"); 53 builder.AppendLine(" <img width='630' height='22' style='width: 630px; height: 22px;' src='{0}' />"); 54 builder.AppendLine(" </div>"); 55 builder.AppendLine(" <div style='color: red; width: 630px; height: 22px;border: 1px solid black;'>这是一封测试邮件</div>"); 56 builder.AppendLine(" <div style='width: 630px; height: 22px;'>"); 57 builder.AppendLine(" <img src='{1}' />");// html显示图片 58 builder.AppendLine(" </div>"); 59 builder.AppendLine(" </body>"); 60 builder.AppendLine("</html>"); 61 var dto = new SendEmailDto 62 { 63 Subject = $"测试Net6邮件[HtmlText] - {DateTime.Now:yyyy-MM-dd HH:mm:ss}", 64 ToList = new List<EmailTargetDto> 65 { 66 new EmailTargetDto 67 { 68 EmailAccount = _email.EmailFromAccount, 69 EmailAddress = _email.EmailFromAddress, 70 } 71 }, 72 Content = builder.ToString(), 73 HtmlImgPathList = new List<string> 74 { 75 @"wwwroot\Email\EmailTop.png", 76 @"wwwroot\Email\Koala.jpg", 77 } 78 79 }; 80 _emailUnit.SendHtmlText(dto); 81 } 82 83 /// <summary> 84 /// 发送Html相对路径 85 /// </summary> 86 [Fact] 87 public void SendHtmlPathDto() 88 { 89 var dto = new SendEmailDto 90 { 91 Subject = $"测试Net6邮件[HtmlPath] - {DateTime.Now:yyyy-MM-dd HH:mm:ss}", 92 ToList = new List<EmailTargetDto> 93 { 94 new EmailTargetDto 95 { 96 EmailAccount = _email.EmailFromAccount, 97 EmailAddress = _email.EmailFromAddress, 98 } 99 }, 100 HtmlPath = @"wwwroot\Email\Email.html", 101 HtmlImgPathList = new List<string> 102 { 103 @"wwwroot\Email\EmailTop.png", 104 @"wwwroot\Email\Koala.jpg", 105 }, 106 AttachmentPathList = new List<string> 107 { 108 @"wwwroot\Email\EmailTop.png", 109 @"wwwroot\Email\Koala.jpg", 110 }, 111 }; 112 _emailUnit.SendHtmlPath(dto); 113 } 114 }