Sending HTML emails with embedded images in BizTalk#
Ever wanted to send emails from BizTalk (using the SMTP adapter) which have images embedded in them? I did recently.

Sending HTML emails (or text emails) in BizTalk without embedded images has been done to death – all you need is the RawString message type.

But how do you use the SMTP adapter to send emails with embedded images?
I didn't want to cheat and write a helper class.

I first looked at using the MIME/SMIME encoder pipeline component.
By default the MIME encoder uses a mime Content-Type of multipart/mixed.

So if you have a multipart message with an HTML body part and a single image as a second part, this is what the MIME encoder generates:

MIME-Version: 1.0
Content-Type: multipart/mixed;
    boundary="--=_NextPart_001_000D_01C79667.1A2EEFB0"
Return-Path: donotreply@acme.com

This is a multi-part message in MIME format.

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: text/html; charset=UTF-8;
    charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/

<html></html>

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>


However, multipart/mixed is not how you embed content – you need to use multipart/related, to indicate that all the parts in the message are related.

(Note: in order to view the output of the SMTP adapter I used the IIS SMTP service, and configured the SMTP adapter to use an SMTP server of localhost, and a To Address which matched my local domain. This will generate EML files in \Inetpub\mailroot\Drop which you can open using Outlook Express (or notepad)).

There's a little known MIME context property you can set called IsMultipartRelated.
If you use this, and have all your images as parts to the message, with your HTML part as the body part, then the SMTP adapter will send a message which can be viewed in a few email clients e.g. Outlook.
If you use this property, this is what the MIME encoder will output:

MIME-Version: 1.0
Content-Type: multipart/related;
    type="text/html";
    boundary="--=_NextPart_001_000D_01C79667.1A2EEFB0"
Return-Path: donotreply@acme.com

This is a multi-part message in MIME format.

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: text/html; charset=UTF-8;
    charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/

<html></html>

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>


However this doesn't work for any web based browsers e.g. Hotmail, Yahoo, SquirrelMail, or LotusNotes.

Reading the RFC for Multipart HTML messages showed me what I was missing: The Content-Type for the complete message needs to be multipart/related, but the first (body) part needs to consist of two parts, and have a Content-Type of multipart/alternative. - that is, the first part is actually another multi-part message!
The reason for this is the body part contains a Text part, and an HTML part – so that browsers which don't understand HTML can still display the message.
It appears that a lot of email browsers won't display messages unless they are in this format and, in fact, this is what the RFC recommends.

Problem is, the MIME encoder does not support this (as far as I can make out – there's a promising property called PartContentTypeSecondaryHeader but I couldn't get it to work for me, plus IBaseMessagePart doesn't act as a container for other parts, so it's unclear how you'd represent it in a message anyway).

So I wrote a pipeline component which takes an HTML stream as input, parses it and downloads all the resources, and then does the correct MIME encoding.
This is the output from my component:

MIME-Version: 1.0
Content-Type: multipart/related;
    type="multipart/alternative";
    boundary="--=_NextPart_001_000D_01C79667.1A2EEFB0"
Return-Path: donotreply@acme.com

This is a multi-part message in MIME format.

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: multipart/alternative;
    boundary="--=_NextPart_001_000E_01C79667.1A2EEFB0"


----=_NextPart_001_000E_01C79667.1A2EEFB0
Content-Type: text/plain;
    charset="iso-8859-1"
Content-Transfer-Encoding: 8bit

Plain text version of the email

----=_NextPart_001_000E_01C79667.1A2EEFB0
Content-Type: text/html;
    charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/

<html></html>

----=_NextPart_001_000E_01C79667.1A2EEFB0--

----=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>


The next trick was trying to get the SMTP adapter to accept the output from my component:
In BizTalk, there are two ways of MIME encoding content: simple encoding is performed by the SMTP adapter (e.g. if you don't use the MIME encoder); more advanced encoding is performed by the MIME encoder.

So obviously the MIME encoder sets a property which indicates that the message is already MIME encoded so the SMTP adapter doesn't need to bother.
If you look at the list of context properties which the MIME encoder supports, you see there's one called IsMIMEEncoded.
Seems pretty obvious.

Except that the property doesn't work.


I had to add a Debug pipeline component which would dump all the context properties attached to a message after the MIME encoder had finished with it to find the answer.
Turns out there's another context property called MimeEncoded (note the lack of capitalisation), which is in the System namespace (not the MIME namespace) and can only be set from code.
And this is the one that the SMTP adapter looks for. You can set it like this:
    inmsg.Context.Write("MimeEncoded",    "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);

And now it works: I get email with embedded resources which can viewed correctly in all mail browsers (except Lotus Notes, which occasionally doesn't show images – but that's Notes for you!)

As an aside, whenever I write a pipeline component, I always try and have it work in a streaming fashion (check out Christof Claessens' great article about this).
However when I reflected over the source of the MIME encoder, I noticed that it doesn't chain the streams – during the IComponent.Execute()call, the encoder reads the streams of all parts and returns a new message with a single part. So it's not implemented in a streaming fashion (i.e. you can't chain together all the streams from all components).

What I haven't covered in this post is how to use Content-ID and the cid: prefix (in your html) to link the embedded resources to the parts in your mime message – you can see examples of this in the RFC.

Tuesday, June 12, 2007 10:01:16 AM (GMT Daylight Time, UTC+01:00) #    Comments [0]  |  Trackback Tracked by:
"http://morningside.edu/mics/_notes/pages/cialis/index.html" (http://morningside... [Pingback]
"http://morningside.edu/mics/_notes/pages/tramadol/index.html" (http://morningsi... [Pingback]
"http://morningside.edu/mics/_notes/pages/clomid/index.html" (http://morningside... [Pingback]
"http://morningside.edu/mics/_notes/pages/ultram/index.html" (http://morningside... [Pingback]
"http://morningside.edu/mics/_notes/pages/claritin/index.html" (http://morningsi... [Pingback]
"http://morningside.edu/mics/_notes/pages/lexapro/index.html" (http://morningsid... [Pingback]
"http://morningside.edu/mics/_notes/pages/paxil/index.html" (http://morningside.... [Pingback]
"http://blastpr.com/wiki/js/pages/nexium/index.html" (http://blastpr.com/wiki/js... [Pingback]
"http://blastpr.com/wiki/js/pages/tramadol/index.html" (http://blastpr.com/wiki/... [Pingback]
"http://morningside.edu/mics/_notes/pages/celebrex/index.html" (http://morningsi... [Pingback]
"http://morningside.edu/mics/_notes/pages/lipitor/index.html" (http://morningsid... [Pingback]
"http://blastpr.com/wiki/js/pages/soma/index.html" (http://blastpr.com/wiki/js/p... [Pingback]
"http://blastpr.com/wiki/js/pages/celebrex/index.html" (http://blastpr.com/wiki/... [Pingback]
"http://blastpr.com/wiki/js/pages/melatonin/index.html" (http://blastpr.com/wiki... [Pingback]
"http://blastpr.com/wiki/js/pages/wellbutrin/index.html" (http://blastpr.com/wik... [Pingback]
"http://blastpr.com/wiki/js/pages/lipitor/index.html" (http://blastpr.com/wiki/j... [Pingback]
"http://morningside.edu/mics/_notes/pages/melatonin/index.html" (http://mornings... [Pingback]
"http://morningside.edu/mics/_notes/pages/prozac/index.html" (http://morningside... [Pingback]
"http://blastpr.com/wiki/js/pages/viagra/index.html" (http://blastpr.com/wiki/js... [Pingback]
"http://blastpr.com/wiki/js/pages/effexor/index.html" (http://blastpr.com/wiki/j... [Pingback]
"http://morningside.edu/mics/_notes/pages/rainbow-brite/index.html" (http://morn... [Pingback]
"http://blastpr.com/wiki/js/pages/synthroid/index.html" (http://blastpr.com/wiki... [Pingback]
"http://blastpr.com/wiki/js/pages/cymbalta/index.html" (http://blastpr.com/wiki/... [Pingback]
"http://blastpr.com/wiki/js/pages/rainbow-brite/index.html" (http://blastpr.com/... [Pingback]
"http://morningside.edu/mics/_notes/pages/prilosec/index.html" (http://morningsi... [Pingback]
"http://morningside.edu/mics/_notes/pages/cymbalta/index.html" (http://morningsi... [Pingback]

 

All content © 2020, Daniel Probert
On this page
This site
Calendar
<July 2020>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Archives
Sitemap
Blogroll OPML
Disclaimer

Powered by: newtelligence dasBlog 2.3.12105.0

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail

Theme design by Jelle Druyts


Pick a theme: