Wednesday, May 14, 2008

Email messages with embedded images

Finally stumbled upon the way to create email messages with embedded images in ASP.NET 2.0. (Note that this doesn't work for ASP.NET 1.1, sorry to say.)

For review ... you can add an image to an email message in these ways:

* Attach it. Works ok, but it's ... attached. Illustrated earlier.
* In an HTML-formatted message, create an tag that points to an absolute URL.
* In an HTML-formatted message, create an tag that points to an embedded image and then (duh) embed the image. In that case, the image shows up inline with the message's text.

Pointing to an absolute URL keeps the message size down, but you have no control over the image on the server, and it could go away or change. Attaching and embedding keep a copy of the image with the message, but bloat the message size.

So, embedding. The trick, such as it is, is to create an alternative view and to add a linked resource to the alternative view. Alternative views enable you to create different versions of the email message -- typically one in plain text and the other formatted with HTML. These then substitute for the basic msg.Body property. Behind the scenes, the class creates the appropriately formatted multipart email that incorporates the alternative views, which lets the email client choose which one to use.

To do the actual embedding, you need to do a number of things:

* Create an HTML-formatted message.
* Use an tag in the message body.
* For the src attribute of the , point not to a URL, but to a content ID (cid). This points to the portion of the message containing the image stream.
* Create an alternative view.
* Create a linkedResource that slurps up the image you want to embed.
* Assign a content ID to the linked resource -- this should match the cid you used in the tag.
* Assign the image's file name to the linked resource.

(I think it takes more lines to describe it than to actually do it.)

Here's code. I can't take credit for it. I played with this for a long time trying to get it to work and came close, but I ultimately relied on an example provided by mharder in an internal email. Note the syntax of the tag and the use of ContentId, ContentType.Name, and filename.

Imports System.Net.Mail
Imports System.Net.Mime
Imports System.IO


Dim fromAddress As String = ""
Dim toAddress As String = ""
Dim subject As String = "Test EmbeddedImage"
Dim contentId As String = "image1"
Dim path As String = Server.MapPath("~") & "\"
Dim filename As String = path & "MyPicture.jpg"
Dim body As String = "Here is a linked resource: "

Dim mailMessage As New MailMessage(fromAddress, toAddress)
mailMessage.Subject = "Testing embedded image"
Dim av1 As AlternateView
av1 = AlternateView.CreateAlternateViewFromString(body, Nothing, _
Dim linkedResource As LinkedResource = New LinkedResource(filename)
linkedResource.ContentId = contentId
linkedResource.ContentType.Name = filename

mailMessage.IsBodyHtml = True
Dim mailSender As New SmtpClient("smtpHost")
labelStatus.Text = "Message sent!"
Catch ex As Exception
labelStatus.Text = ex.Message
End Try