Save images embedded in HTML email to disk using IMAP

This article shows how to save all images embedded in HTML email message to disk. The messages are downloaded using Mail.dll .NET IMAP library and IMAP protocol.

In most cases HTML body of the message references such images using special “cid:” protocol, that specifies Content-ID of the image that should be used:

This is our <strong>brand new</strong> logo: <br />
<img src="cid:logo@example.com" />

In such case actual image is embedded inside an email, as part of the mime tree, as an element related to HTML body and with content-disposition header set to inline. This means that invoking GetMessageByUID method is going to download entire email message, including all images. This makes message bigger, but images don’t need to be stored on your web server (remember that emails tend to be archived for years).

Mail.dll exposes all attachments as well-known .NET collections. There are 4 collections that may contain attachments:

  • IMail.Attachments – all attachments (includes Visuals, NonVisuals and Alternatives).
  • IMail.Visuals – visual elements, files that should be displayed to the user, such as images embedded in an HTML email.
  • IMail.NonVisuals – non visual elements, “real” attachments.
  • IMail.Alternatives – alternative content representations, for example ical appointment.

As you can see the most interesting from this article’s point of view is IMail.Visuals collection. As with regular attachments, visual elements are represented by MimeData objects.

// C#

using Limilabs.Client.IMAP;
using Limilabs.Mail;
using Limilabs.Mail.MIME;
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
       using (Imap imap = new Imap())
       {
           imap.Connect("imap.example.com");
           imap.UseBestLogin("user", "password");

           imap.SelectInbox();

           List<long> uids = imap.Search(Flag.Unseen);
           foreach (long uid in uids)
           {
               var eml = imap.GetMessageByUID(uid);
               IMail email = new MailBuilder()
                   .CreateFromEml(eml);
               Console.WriteLine(email.Subject);

               foreach (MimeData mime in email.Visuals)
               {
                   mime.Save(@"c:\" + mime.SafeFileName);
               }
           }
           imap.Close();
       }
    }
};
' VB.NET

Imports Limilabs.Client.IMAP
Imports Limilabs.Mail
Imports Limilabs.Mail.MIME
Imports System
Imports System.Collections.Generic

Public Module Module1
    Public Sub Main(ByVal args As String())

        Using imap As New Imap()
            imap.Connect("imap.example.com")
            imap.UseBestLogin("user", "password")

            imap.SelectInbox()
            Dim uids As List(Of Long) = imap.Search(Flag.Unseen)

            For Each uid As Long In uids
                Dim eml = imap.GetMessageByUID(uid)
                Dim email As IMail = New MailBuilder() _
                    .CreateFromEml(eml)
                Console.WriteLine(email.Subject)

                For Each mime As MimeData In email.Attachments
                    mime.Save("c:\" + mime.SafeFileName)
                Next
            Next
            imap.Close()
        End Using

    End Sub
End Module

You can also save attachment to stream MimeData.Save(Stream stream), get direct access to it as stream MemoryStream MimeData.GetMemoryStream()
or as byte array using byte[] MimeData.Data property.

You can also use SaveHtmlAs method to save entire message as regular HTML page with all required images and styles to a single folder:

// C#

IMail email = ...
email.SaveHtmlAs(@"c:\tmp\email.html");
' VB.NET

Dim email As IMail = ...
email.SaveHtmlAs("c:\tmp\email.html")

What is also worth mentioning is the fact that you can use Content-Id to search through IMail.Visuals collection:

// C#

IMail email = ...;
MimeData logo = email.Visuals["logo@example.com"];
// VB.NET

Dim email As IMail = ...
Dim logo As MimeData = email.Visuals("logo@example.com")

Tags: , , , , ,

Questions?

Consider using our Q&A forum for asking any questions.