C# sending mails with images inline using SmtpClient
Asked Answered
C

6

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.

Canal answered 31/7, 2009 at 14:27 Comment(2)
@JamesMcCormack I must have missed that all that time ago. UpdatedCanal
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. #33665780Studdard
H
88

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("[email protected]"));
    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.

Hypha answered 12/6, 2012 at 16:37 Comment(14)
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.Stambaugh
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.Hypha
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.Fiveandten
YOU ARE A SAVER. after 2 days of searching ...ohChloroform
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!Unerring
How would you use: Server.MapPath() in Powershell?Bradlybradman
That was just an example, why not just use the full path to your file as the argument to LinkedResource() instead.Hypha
Server.MapPath -> Where is "Server" defined?Mcclary
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?Mcclary
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.5Mcclary
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 ..Abingdon
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.Serrato
@Mcclary 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 :-)Warthog
@JamesMcCormack what if there are 3 logos in the mail what to do then ?Hide
S
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

Stambaugh answered 31/7, 2009 at 14:31 Comment(6)
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.Hypha
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.Bradlybradman
You shouldn't use a random number generator when you require unique ids. Use GUIDs or something else which is guaranteed to be unique.Term
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.Stambaugh
If its content disposition is "inline" instead of "attachment" it will not show up in the mail clients attachments (doesn't in outlook).Citole
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.Herson
T
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;
Thad answered 31/7, 2009 at 14:33 Comment(1)
your link in the answer is not active longer?Cardiganshire
M
2

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

Take a look here.

Maturate answered 31/7, 2009 at 14:31 Comment(6)
I'm still convinced this has to be considered as an option.. sweeting.org/mark/blog/2005/07/12/…Maturate
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.Stambaugh
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: 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.Maturate
Why was this voted down? It is correct. You can embedd images in a data url.Shir
There are some limitations (i.e. size) regarding images in Base64 strings I think.Admire
The question is about email, not HTML. Many popular mail clients (e.g. all versions of Outlook) will not display Base64-encoded images.Term
M
1

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.

Mandie answered 18/9, 2017 at 21:48 Comment(0)
B
-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.

Backandforth answered 31/7, 2009 at 14:31 Comment(2)
Read the question again. He wants to attach the image and show it in the body.Dairying
This is really better placed as a comment to the accepted answer.Erkan

© 2022 - 2024 — McMap. All rights reserved.