如何将 MailMessage 对象保存为 * . eml 或 * . msg 文件

如何将 MailMessage 对象保存到磁盘? MailMessage 对象不公开任何 Save ()方法。

我没有问题,如果它保存在任何格式,* . eml 或 * . msg。有什么想法如何做到这一点?

98715 次浏览

For simplicity, I'll just quote an explanation from a Connect item:

You can actually configure the SmtpClient to send emails to the file system instead of the network. You can do this programmatically using the following code:

SmtpClient client = new SmtpClient("mysmtphost");
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = @"C:\somedirectory";
client.Send(message);

You can also set this up in your application configuration file like this:

 <configuration>
<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="C:\somedirectory" />
</smtp>
</mailSettings>
</system.net>
</configuration>

After sending the email, you should see email files get added to the directory you specified. You can then have a separate process send out the email messages in batch mode.

You should be able to use the empty constructor instead of the one listed, as it won't be sending it anyway.

Here's an extension method to convert a MailMessage to a stream containing the EML data. Its obviously a bit of a hack as it uses the file system, but it works.

public static void SaveMailMessage(this MailMessage msg, string filePath)
{
using (var fs = new FileStream(filePath, FileMode.Create))
{
msg.ToEMLStream(fs);
}
}


/// <summary>
/// Converts a MailMessage to an EML file stream.
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static void ToEMLStream(this MailMessage msg, Stream str)
{
using (var client = new SmtpClient())
{
var id = Guid.NewGuid();


var tempFolder = Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name);


tempFolder = Path.Combine(tempFolder, "MailMessageToEMLTemp");


// create a temp folder to hold just this .eml file so that we can find it easily.
tempFolder = Path.Combine(tempFolder, id.ToString());


if (!Directory.Exists(tempFolder))
{
Directory.CreateDirectory(tempFolder);
}


client.UseDefaultCredentials = true;
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = tempFolder;
client.Send(msg);


// tempFolder should contain 1 eml file


var filePath = Directory.GetFiles(tempFolder).Single();


// stream out the contents
using (var fs = new FileStream(filePath, FileMode.Open))
{
fs.CopyTo(str);
}


if (Directory.Exists(tempFolder))
{
Directory.Delete(tempFolder, true);
}
}
}

You can then take the stream thats returned and do as you want with it, including saving to another location on disk or storing in a database field, or even emailing as an attachment.

For one reason or another the client.send failed (right after an actual send using that method) so I plugged in good 'ole CDO and ADODB stream. I also had to load CDO.message with a template.eml before setting the .Message values. But it works.

Since the above one is C here is one for VB

    MyMessage.From = New Net.Mail.MailAddress(mEmailAddress)
MyMessage.To.Add(mToAddress)
MyMessage.Subject = mSubject
MyMessage.Body = mBody


Smtp.Host = "------"
Smtp.Port = "2525"
Smtp.Credentials = New NetworkCredential(------)


Smtp.Send(MyMessage)        ' Actual Send


Dim oldCDO As CDO.Message
oldCDO = MyLoadEmlFromFile("template.eml")  ' just put from, to, subject blank. leave first line blank
oldCDO.To = mToAddress
oldCDO.From = mEmailAddress
oldCDO.Subject = mSubject
oldCDO.TextBody = mBody
oldCDO.HTMLBody = mBody
oldCDO.GetStream.Flush()
oldCDO.GetStream.SaveToFile(yourPath)

If you are using Mailkit. Just write below code

string fileName = "your filename full path";
MimeKit.MimeMessage message = CreateMyMessage ();
message.WriteTo(fileName);

try this

please use these 2 reference ( using MailBee;) ( using MailBee.Mime;)

    public static string load(string to,string from,string cc,string bcc,string subject,string body, List<string> reportList,string path, bool HtmlbodyType)
{
try
{
MailBee.Mime.MailMessage msg = new MailBee.Mime.MailMessage();
msg.From.AsString = from;
msg.Subject = subject;
if (HtmlbodyType == true)
{
msg.BodyHtmlText = body;
}
else
{
msg.BodyPlainText = body;
}
            

string[] receptionEmail = to.Split(new string[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries);
string[] ccEmail = cc.Split(new string[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries);
string[] bccEmail = bcc.Split(new string[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries);
string message = "";
foreach (string To in receptionEmail)
{
msg.To.Add(To);
}
foreach (string CC in ccEmail)
{
msg.Cc.Add(CC);
}
foreach (string Bcc in bccEmail)
{
msg.Bcc.Add(Bcc);


}
for (int x = 0; x < reportList.Count; x++)
{
string fileName = reportList[x];
msg.Attachments.Add(fileName);
}


msg.SaveMessage(path);
return "Success";
            

}
catch (Exception ex)
{
return ex.Message;
}


}

With the help of community I came up with an solution for .NET 5. I have combined this old solution with suggestions in this post and got inspired by Mailkit which resulted in nice extension method without unnecessary dependencies

public static class MailMessageHelper
{
public static void WriteTo(this MailMessage mail, Stream stream)
{
Assembly assembly = typeof(SmtpClient).Assembly;
Type _mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");


// Get reflection info for MailWriter contructor
ConstructorInfo _mailWriterConstructor =
_mailWriterType.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[] { typeof(Stream), typeof(bool) },
null);


// Construct MailWriter object with our FileStream
object _mailWriter =
_mailWriterConstructor.Invoke(new object[] { stream, true });


// Get reflection info for Send() method on MailMessage
MethodInfo _sendMethod =
typeof(MailMessage).GetMethod(
"Send",
BindingFlags.Instance | BindingFlags.NonPublic);


// Call method passing in MailWriter
_sendMethod.Invoke(
mail,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { _mailWriter, true, true },
null);


// Finally get reflection info for Close() method on our MailWriter
MethodInfo _closeMethod =
_mailWriter.GetType().GetMethod(
"Close",
BindingFlags.Instance | BindingFlags.NonPublic);


// Call close method
_closeMethod.Invoke(
_mailWriter,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
Array.Empty<object>(),
null);
}
}

Usage

MailMessage mail = new(mailFrom, mailTo, mailSubject, mailContent);
mail.WriteTo(new FileStream(@"path_to_file\new_mail.eml", FileMode.Create));

Also if you are using MemoryStream and want to get result in string, just change the return type of the extension method and at the end write

return Encoding.ASCII.GetString(stream.ToArray());

Enjoy