40

SmtpClient() allows you to add attachments to your mails, but what if you wanna make an image appear when the mail opens, instead of attaching it?

As I remember, it can be done with about 4 lines of code, but I don't remember how and I can't find it on the MSDN site.

EDIT: I'm not using a website or anything, not even an IP address. The image(s) are located on a harddrive. When sent, they should be part of the mail. So, I guess I might wanna use an tag... but I'm not too sure, since my computer isn't broadcasting.

KdgDev
  • 14,299
  • 46
  • 120
  • 156
  • @JamesMcCormack I must have missed that all that time ago. Updated – KdgDev Aug 11 '15 at 15:27
  • Check this link. it has ready to use method for multiple inline attachment for png filestream as well as for general attachment for pdf/excel filestream. https://stackoverflow.com/questions/33665280/add-multiple-images-in-the-email-body-inlineusing-c-sharp-windows-application/49329461#49329461 – kumar chandraketu Mar 16 '18 at 21:19

6 Answers6

87

One solution that is often mentioned is to add the image as an Attachment to the mail, and then reference it in the HTML mailbody using a cid: reference.

However if you use the LinkedResources collection instead, the inline images will still appear just fine, but don't show as additional attachments to the mail. That's what we want to happen, so that's what I do here:

using (var client = new SmtpClient())
{
    MailMessage newMail = new MailMessage();
    newMail.To.Add(new MailAddress("you@your.address"));
    newMail.Subject = "Test Subject";
    newMail.IsBodyHtml = true;

    var inlineLogo = new LinkedResource(Server.MapPath("~/Path/To/YourImage.png"), "image/png");
    inlineLogo.ContentId = Guid.NewGuid().ToString();

    string body = string.Format(@"
            <p>Lorum Ipsum Blah Blah</p>
            <img src=""cid:{0}"" />
            <p>Lorum Ipsum Blah Blah</p>
        ", inlineLogo.ContentId);

    var view = AlternateView.CreateAlternateViewFromString(body, null, "text/html");
    view.LinkedResources.Add(inlineLogo);
    newMail.AlternateViews.Add(view);

    client.Send(newMail);
}

NOTE: This solution adds an AlternateView to your MailMessage of type text/html. For completeness, you should also add an AlternateView of type text/plain, containing a plain text version of the email for non-HTML mail clients.

James McCormack
  • 9,217
  • 3
  • 47
  • 57
  • Interesting but how does that present to a mail client that only presents the text? Are the images then available as attachments? I ask out of curiosity, I've never tried that myself. – Lazarus Jun 21 '12 at 17:42
  • 1
    Best practice is to include 2 AlternateViews in the mail, the first being of type "text/plain" containing a plaintext mail body. The second View would be the "text/html" View I detailed above. – James McCormack Jun 21 '12 at 22:08
  • This works extremely well. Needed some slight modifying in order to use images included in the project and an html file containg the content with placeholders for the cids. – Ellesedil Feb 27 '13 at 15:11
  • 1
    YOU ARE A SAVER. after 2 days of searching ...oh – Ehsan Zargar Ershadi May 28 '13 at 00:02
  • 2
    This is a great solution. Thank you for adding it. I would also suggest adding how to do it with an Image element. I managed that using a MemoryStream with LinkedResource. Worked great, ... once I reset the .Position property after getting a Stream! – Matthew M. Sep 10 '13 at 16:15
  • How would you use: Server.MapPath() in Powershell? – David Klempfner Feb 21 '14 at 03:53
  • That was just an example, why not just use the full path to your file as the argument to LinkedResource() instead. – James McCormack Feb 21 '14 at 17:04
  • 2
    Server.MapPath -> Where is "Server" defined? – ThunderGr Apr 22 '14 at 06:46
  • Aren't you supposed to dispose newMail, as well? I mean, the SMTPClient is disposed because of the using statement, but where is the MailMessage disposed? – ThunderGr Apr 22 '14 at 07:09
  • 4
    In order for me to get this working, I had to include the MIME content in the creation of LinkedResource("image/png"). I use .Net 4.5 – ThunderGr Apr 22 '14 at 09:59
  • Not working for me(NET 4.0). It sends my image as attachment and it's showing empty frame instead of image inside my mail content .. – MajkeloDev Dec 31 '14 at 09:54
  • How to use this solution with MailNotification class and html template for e-mail. This line of code "NewMail.AlternateViews.Add(view);" overrides the html template that is defined threw property BodyFileName of the MailNotification object. As a result, only image is shown in mail, and not the html template. – Gašper Sladič Mar 12 '15 at 09:11
  • @ThunderGr You saved my efforts !! Without setting mime type while created LinkedResource, it comes as attachment ATT0001.BIN in some client like Windows Mail APP on Windows 10... Thanks :-) – Milind Thakkar Jun 18 '17 at 18:25
  • @JamesMcCormack what if there are 3 logos in the mail what to do then ? –  Jul 10 '17 at 10:55
13

The HTML Email and the images are attachments so it's just a case of referring to the image(s) by their content ids, i.e.

    Dim A As System.Net.Mail.Attachment = New System.Net.Mail.Attachment(txtImagePath.Text)
    Dim RGen As Random = New Random()
    A.ContentId = RGen.Next(100000, 9999999).ToString()
    EM.Body = "<img src='cid:" + A.ContentId +"'>" 

There seems to be comprehensive examples here: Send Email with inline images

Lazarus
  • 41,906
  • 4
  • 43
  • 54
  • This works and the image does appear within the right place in the mail body, but I'm guessing the OP doesn't want the image to appear in the list of attachments as well. I believe the correct answer is to use a LinkedResource - see my answer. – James McCormack Jun 12 '12 at 16:21
  • what is cid? Do we need to set some settings on the mailmessage object to be able to use MIME? Also, why did you use a random number between 100000, 9999999 for the content id? I'm using your code in powershell, but the image is displaying as a red cross. – David Klempfner Feb 21 '14 at 04:32
  • You shouldn't use a random number generator when you require unique ids. Use GUIDs or something else which is *guaranteed* to be unique. – Dan Bechard Sep 09 '15 at 20:02
  • It doesn't need to be globally unique but it's just a question of taste in the end. This is just an example not meant as production ready code. – Lazarus Sep 10 '15 at 19:09
  • If its content disposition is "inline" instead of "attachment" it will not show up in the mail clients attachments (doesn't in outlook). – Sam Nov 26 '15 at 06:12
  • Lazarus; thanks a million for this. Been trying to fix this for days and all I was missing was this reference to contentid. This works brilliantly. – vbNewbie May 12 '16 at 15:35
13

When you say 4 lines of code, are you referring to this?

System.Net.Mail.Attachment inline = new System.Net.Mail.Attachment(@"imagepath\filename.png");
inline.ContentDisposition.Inline = true;
Brandon
  • 68,708
  • 30
  • 194
  • 223
2

What about converting images in Base64 strings? AFAIK this can be easily embedded within mail body.

Take a look here.

ccalboni
  • 12,120
  • 5
  • 30
  • 38
  • I'm still convinced this has to be considered as an option.. http://www.sweeting.org/mark/blog/2005/07/12/base64-encoded-images-embedded-in-html – ccalboni Jul 31 '09 at 14:46
  • The image will be base64 encoded anyway, that's nothing to do with making it appear within an HTML email. The -1 wasn't from me but the answer is a little wide of the mark. – Lazarus Oct 14 '09 at 11:28
  • All I want to say is that if you want to embed an image inside an HTML document, you can. Just encode it in base64 and place the resultant string inside the src attribute of img tag. Take a look here: http://www.tekasarts.com/stuff/base_64_img.html What I did was: - gradient-fill a gif in photoshop - Convert.ToBase64String() image bytes - paste to an HTML document As far as I can understand this may not be a preferred solution but it's in topic and in a certain way a pretty good solution. – ccalboni Oct 23 '09 at 17:38
  • Why was this voted down? It is correct. You can embedd images in a data url. – usr Oct 13 '11 at 16:01
  • There are some limitations (i.e. size) regarding images in Base64 strings I think. – Żubrówka Mar 13 '13 at 11:56
  • 4
    The question is about email, not HTML. Many popular mail clients (e.g. all versions of Outlook) will not display Base64-encoded images. – Dan Bechard Sep 09 '15 at 20:05
0

The solution already posted is the best I have found, I'm just going to complete it with for example if you have multiple images.

        string startupPath = AppDomain.CurrentDomain.RelativeSearchPath;
        string path = Path.Combine(startupPath, "HtmlTemplates", "NotifyTemplate.html");
        string body = File.ReadAllText(path);

        //General tags replacement.
        body = body.Replace("[NOMBRE_COMPLETO]", request.ToName);
        body = body.Replace("[ASUNTO_MENSAJE]", request.Subject);

        //Image List Used to replace into the template.
        string[] imagesList = { "h1.gif", "left.gif", "right.gif", "tw.gif", "fb.gif" };

        //Here we create link resources one for each image. 
        //Also the MIME type is obtained from the image name and not hardcoded.
        List<LinkedResource> imgResourceList = new List<LinkedResource>();
        foreach (var img in imagesList)
        {
            string imagePath = Path.Combine(startupPath, "Images", img);
            var image = new LinkedResource(imagePath, "image/" + img.Split('.')[1]);
            image.ContentId = Guid.NewGuid().ToString();
            image.ContentType.Name = img;
            imgResourceList.Add(image);
            body = body.Replace("{" + Array.IndexOf(imagesList, img) + "}", image.ContentId);
        }

        //Altern view for managing images and html text is created.
        var view = AlternateView.CreateAlternateViewFromString(body, null, "text/html");
        //You need to add one by one each link resource to the created view
        foreach (var imgResorce in imgResourceList)
        {
            view.LinkedResources.Add(imgResorce);
        }

        ThreadPool.QueueUserWorkItem(o =>
        {
            using (SmtpClient smtpClient = new SmtpClient(servidor, Puerto))
            {
                smtpClient.EnableSsl = true;
                smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                smtpClient.Timeout = 50000;
                smtpClient.UseDefaultCredentials = false;
                smtpClient.Credentials = new System.Net.NetworkCredential()
                {
                    UserName = UMail,
                    Password = password
                };
                using (MailMessage mailMessage = new MailMessage())
                {
                    mailMessage.IsBodyHtml = true;
                    mailMessage.From = new MailAddress(UMail);
                    mailMessage.To.Add(request.ToEmail);
                    mailMessage.Subject = "[NAPNYL] " + request.Subject;
                    mailMessage.AlternateViews.Add(view);
                    smtpClient.Send(mailMessage);
                }
            }
        });

As you can see you have an array of image names, it is important that the images are in the same folder because it is pointing to the same output folder.

Finally the email is sent as async so the user doesn't have to wait for it to be sent.

Nelson Parra
  • 101
  • 1
  • 5
-2

The process of making an image appear on the client when the mail is opened is a client function. As long as the client knows how to render the image & has not blocked any image content it will open it right away. You do not have to do anything special while sending the email to make it open on the client as long as you have correctly specified the image mime attachment type.

msvcyc
  • 2,569
  • 4
  • 24
  • 30