1

My .NET 4.5 web application uses class SmtpClient to create and send e-mail messages to various recipients. Each e-mail message consists of:

  • an HTML message body
  • an embedded inline image (JPeg or PNG or GIF)
  • an attachment (PDF)

Sample code is below. It works fine, but there is one gripe from OSX users. Apple's standard mail app renders the image twice; once inlined in the message body, and again following the message body, next to the preview of the PDF attachment.


Screenshot Apple Mail, faulty message


I tinkered with the following properties; none of which would help.

  • SmtpClient's DeliveryFormat
  • MailMessage's IsBodyHtml and BodyTransferEncoding
  • Attachment's MimeType, Inline, DispositionType, ContentId, FileName, Size, CreationDate, ModificationDate

If I compose a similar e-mail message in MS Outlook and send it off to the Apple user, the image is rendered once, inlined in the message body; exactly as I would like it to be. So apparently it is possible. After reading this, I inspected the raw MIME data, and noticed Outlook uses multipart/related to group together the message body and the images.


Screenshot Apple Mail, message from Outlook


My question: How do I mimic Outlook's behavior with the classes found in System.Net.Mail?

Things I would rather not do:

  • Employ external images instead of embedded ones (many e-mail clients initially block these to protect recipient's privacy).
  • Use third party libraries (to avoid legal hassle). The SmtpDirect class I found here seems to solve the problem (though I got a server exception in return), but it is hard for me to accept a complete rewrite of MS's SmtpClient implementation is necessary for such a subtle change.
  • Send the e-mail message to a pickup folder, manipulate the resulting .eml file, push the file to our Exchange server.

Minimal code to reproduce the problem:

using System.IO;
using System.Net.Mail;
using System.Net.Mime;

namespace SendMail
{
    class Program
    {
        const string body = "Body text <img src=\"cid:ampersand.gif\" /> image.";

        static Attachment CreateGif()
        {
            var att = new Attachment(new MemoryStream(Resource1.ampersand), "ampersand.gif")
            {
                ContentId = "ampersand.gif",
                ContentType = new ContentType(MediaTypeNames.Image.Gif)
            };
            att.ContentDisposition.Inline = true;
            return att;
        }

        static Attachment CreatePdf()
        {
            var att = new Attachment(new MemoryStream(Resource1.Hello), "Hello.pdf")
            {
                ContentId = "Hello.pdf",
                ContentType = new ContentType(MediaTypeNames.Application.Pdf)
            };
            att.ContentDisposition.Inline = false;
            return att;
        }

        static MailMessage CreateMessage()
        {
            var msg = new MailMessage(Resource1.from, Resource1.to, "The subject", body)
            {
                IsBodyHtml = true
            };
            msg.Attachments.Add(CreateGif());
            msg.Attachments.Add(CreatePdf());
            return msg;
        }

        static void Main(string[] args)
        {
            new SmtpClient(Resource1.host).Send(CreateMessage());
        }
    }
}

To actually build and run it, you will need an additional resource file Resource1.resx with the two attachments (ampersand and Hello) and three strings host (the SMTP server), from and to (both of which are e-mail addresses).

Community
  • 1
  • 1
Ruud Helderman
  • 9,064
  • 1
  • 19
  • 39

1 Answers1

1

(I found this solution myself before I got to posting the question, but decided to publish anyway; it may help out others. I am still open for alternative solutions!)

I managed to get the desired effect by using class AlternateView.

static MailMessage CreateMessage()
{
    var client = new SmtpClient(Resource1.host);
    var msg = new MailMessage(Resource1.from, Resource1.to, "The subject", "Alternative message body in plain text.");
    var view = AlternateView.CreateAlternateViewFromString(body, System.Text.Encoding.UTF8, MediaTypeNames.Text.Html);
    var res = new LinkedResource(new MemoryStream(Resource1.ampersand), new ContentType(MediaTypeNames.Image.Gif))
    {
        ContentId = "ampersand.gif"
    };
    view.LinkedResources.Add(res);
    msg.AlternateViews.Add(view);
    msg.Attachments.Add(CreatePdf());
    return msg;
}

Screenshot Apple Mail, fixed message


As a side effect, the message now also contains a plain text version of the body (for paranoid web clients that reject HTML). Though it is a bit of a burden ("Alternative message body in plain text" needs improvement), it does give you more control as to how the message is rendered under different security settings.

Ruud Helderman
  • 9,064
  • 1
  • 19
  • 39