Applications typically send a lot of emails. At least, they should, since it’s a good, archive-able method for
communication and confirmation. Archive-able for the receiver, sure, but what about the sender – the
application? You have a couple of options: you can parse out all the fields of the email and stick them in a
database or you can CC or BCC the email to a mailbox for archival. What if you want to store the actual email
that was sent? You want the actual EML file.
Wouldn’t it be nice if the MailMessage object had a .SaveAs method? It doesn’t. Well then, wouldn’t it be
nice if the SmtpClient object had a .SaveTo property? It does, kind of. And by using those properties,
we can capture the actual EML file that is typically sent to a SMTP server for delivery. Once we have that EML
file data, we can save it to a file or to a database or wherever.
Quite simply, you need to set two properties on the SmtpClient object: DeliveryMethod and
PickupDirectoryLocation. This tells the SmtpClient to write the EML to a specified folder and the mail server
will monitor that folder and pull it from there. This is the code:
Private Function GetEmailBytes(ByVal eml As Mail.MailMessage) As Byte()
Dim smtp As Mail.SmtpClient
Dim customFolderName As String
Dim fileBytes() As Byte
customFolderName = IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, Guid.NewGuid.ToString)
IO.Directory.CreateDirectory(customFolderName)
smtp = New Mail.SmtpClient
With smtp
.Host = "localhost"
.DeliveryMethod = Mail.SmtpDeliveryMethod.SpecifiedPickupDirectory
.PickupDirectoryLocation = customFolderName
.Send(eml)
.Dispose()
End With
fileBytes = IO.File.ReadAllBytes(New IO.DirectoryInfo(customFolderName).GetFiles.First.FullName)
IO.Directory.Delete(customFolderName, True)
Return fileBytes
End Function
To explain the extra legwork involving directories, the SmtpClient writes the EML file with a GUID as a
filename. This prevents emails from overwriting each other. In a multi-user environment though, how
could we know which file was just written so we read the right file? So to be sure what we’re reading is our email,
we create a unique folder to write the EML to and we know there will be only one file in there to read.
The GetEmailBytes method just returns the bytes of an EML file. That’s the most flexible way to work with the
data. If you want to save that to another place with another name, just use IO.File.WriteAllBytes, like so:
Private Sub SaveMessage()
Dim eml As Mail.MailMessage
Dim bytes() As Byte
eml = New Mail.MailMessage
With eml
.To.Add(New Mail.MailAddress("anyone@home.com"))
.From = New Mail.MailAddress("nobody@home.com")
.Subject = "test save"
.Body = "this is the body"
End With
bytes = GetEmailBytes(eml)
IO.File.WriteAllBytes(IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "output.eml"), bytes)
eml.Dispose()
End Sub