如何用Graph SDK发送带附件的MIME邮件并避免winmail.dat
问题:通过Microsoft Graph SDK发送HTML邮件带PDF附件避免winmail.dat
我使用C#的Graph SDK从Web应用发送邮件,邮件正文为HTML格式并附带PDF附件。但收件人收到邮件时会附带winmail.dat文件,有时还会出现noname文件,邮件也会以纯文本形式展示。Gmail可以解析winmail.dat,但并非所有客户端都支持。
调研得知需使用MIME格式,但难以找到简洁示例。请问如何通过Graph API/SDK发送带附件的HTML格式MIME邮件,且不会生成winmail.dat?
现有代码
创建MimeMessage的代码
private MimeMessage GetMimeMessage(string fromEmail, string subject, string htmlBody, IEnumerable<string> toRecipients, IEnumerable<string> ccRecipients, IEnumerable<string> attachments) { var message = new MimeMessage(); foreach (var recipient in toRecipients) message.To.Add(MailboxAddress.Parse(recipient)); foreach (var recipient in ccRecipients) message.Cc.Add(MailboxAddress.Parse(recipient)); message.From.Add(MailboxAddress.Parse(fromEmail)); message.Subject = subject; var builder = new BodyBuilder(); builder.TextBody = htmlBody; builder.HtmlBody = htmlBody; foreach (var attachment in attachments) builder.Attachments.Add(attachment); message.Body = builder.ToMessageBody(); return message; }
发送邮件的代码
private async Task SendMimeMessageWithGraph(string sendFromEmail, MimeMessage mimeMessage) { var stream = new MemoryStream(); mimeMessage.WriteTo(stream); stream.Position = 0; StringContent MessagePost = new StringContent(Convert.ToBase64String(stream.ToArray()), Encoding.UTF8, "text/plain"); var message = new Message(); var mypost = await MessagePost.ReadAsStringAsync(); var sendMailRequest = _GraphClient.Users[sendFromEmail].SendMail(message, true).Request().GetHttpRequestMessage(); sendMailRequest.Content = MessagePost; sendMailRequest.Method = HttpMethod.Post; var sendResult = await _GraphClient.HttpProvider.SendAsync(sendMailRequest); Debug.WriteLine(sendResult.StatusCode); }
生成的MIME内容
From: fromemail@fromemail.com Date: Tue, 06 Sep 2022 21:39:47 -0400 Subject: Invoice #0000003 Message-Id: <2FWZXJIFTHU4.JPGM32IG50SI2@person1234> To: Newperson@new.com Cc: cc@cc.cc MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="=-9G8gCZS3S+XQhQ+1kLqUog==" --=-9G8gCZS3S+XQhQ+1kLqUog== Content-Type: text/plain; charset=utf-8 Please see attached invoice.<br><br>Thank you for your business,<br>Test<br>Person<br>222-999-1111<br>email@email.com<br><img height="50px" width="auto" src="data:image/svg+xml;base64,PD94b...>"> --=-9G8gCZS3S+XQhQ+1kLqUog== Content-Type: text/html; charset=utf-8 Please see attached invoice.<br><br>Thank you for your business,<br>Test<br>Person<br>222-999-1111<br>email@email.com<br><img height="50px" width="auto" src="data:image/svg+xml;base64,PD94..."> --=-9G8gCZS3S+XQhQ+1kLqUog==--
问题分析
- 纯文本内容错误:直接将HTML内容赋值给
TextBody,导致纯文本部分包含HTML标签,客户端解析时会优先展示混乱的纯文本内容。 - MIME结构错误:带附件的邮件需要使用multipart/mixed作为外层结构(包含正文部分和附件),现有代码生成的是
multipart/alternative结构,附件被错误处理,进而生成winmail.dat。 - 请求Content-Type错误:发送完整MIME邮件时,Graph API要求的Content-Type是
message/rfc822,而非text/plain。
修正后的代码
正确创建MimeMessage
private MimeMessage GetMimeMessage(string fromEmail, string subject, string htmlBody, IEnumerable<string> toRecipients, IEnumerable<string> ccRecipients, IEnumerable<string> attachments) { var message = new MimeMessage(); foreach (var recipient in toRecipients) message.To.Add(MailboxAddress.Parse(recipient)); foreach (var recipient in ccRecipients) message.Cc.Add(MailboxAddress.Parse(recipient)); message.From.Add(MailboxAddress.Parse(fromEmail)); message.Subject = subject; var builder = new BodyBuilder(); // 将HTML转换为可读的纯文本,避免客户端展示混乱内容 builder.TextBody = HtmlToPlainText(htmlBody); builder.HtmlBody = htmlBody; foreach (var attachment in attachments) builder.Attachments.Add(attachment); message.Body = builder.ToMessageBody(); return message; } // 简易HTML转纯文本方法,可根据实际需求扩展完善 private string HtmlToPlainText(string html) { // 替换换行标签为换行符 var plainText = html.Replace("<br>", "\n").Replace("<br/>", "\n").Replace("<BR>", "\n"); // 移除所有HTML标签 plainText = System.Text.RegularExpressions.Regex.Replace(plainText, "<.*?>", string.Empty); // 合并多余空白字符 plainText = System.Text.RegularExpressions.Regex.Replace(plainText, @"\s+", " ").Trim(); return plainText; }
正确发送MIME邮件
private async Task SendMimeMessageWithGraph(string sendFromEmail, MimeMessage mimeMessage) { using var stream = new MemoryStream(); mimeMessage.WriteTo(stream); stream.Position = 0; // 设置正确的Content-Type为message/rfc822 var messageContent = new StringContent(Convert.ToBase64String(stream.ToArray()), Encoding.UTF8, "message/rfc822"); // 直接构造SendMail请求,无需创建空Message对象 var request = new HttpRequestMessage(HttpMethod.Post, $"https://graph.microsoft.com/v1.0/users/{sendFromEmail}/sendMail"); request.Content = messageContent; var sendResult = await _GraphClient.HttpProvider.SendAsync(request); sendResult.EnsureSuccessStatusCode(); // 确保请求成功,可根据业务需求添加异常处理 Debug.WriteLine(sendResult.StatusCode); }
说明
- MIME结构自动处理:当邮件同时包含正文(文本+HTML)和附件时,MimeKit的
BodyBuilder会自动生成multipart/mixed外层结构,内部包含multipart/alternative(正文部分)和附件,彻底避免winmail.dat的生成。 - 纯文本优化:新增HTML转纯文本的方法,确保纯文本部分为可读的普通文本,客户端会根据自身支持情况选择展示HTML或纯文本内容。
- 请求格式修正:将Content-Type改为
message/rfc822,符合Graph API接受完整MIME邮件的要求。
内容的提问来源于stack exchange,提问作者GisMofx
相关产品推荐
相关产品推荐

