NET6/Framework 封装邮件发送纯文本/HTML/HTML+图片/附件

发布时间 2023-09-21 15:26:38作者: Robot-Blog

发送纯文本

{
    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

  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     }
View Code

Helper

  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     }
View Code

Run

  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     }
View Code