GMail not showing inline-images (cid) i'm sending with System.Net.Mail
Asked Answered
M

4

7

When I send an email via outlook or gmail to a gmail email address I can add inline-images which are directly shown in the gmail webinterface:

GMail webinterface with working inline image

Relevant raw mail-header and raw body parts of the working email:

--089e0158b6909948880520cef593
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Image there?<div><img src=3D"cid:ii_if3zqhar0_15014363be0a=
41b2" width=3D"10" height=3D"3"><br>=E2=80=8BHope so!<br></div></div>

--089e0158b6909948880520cef593--
--089e0158b69099488c0520cef594
Content-Type: image/png; name="test.png"
Content-Disposition: inline; filename="test.png"
Content-Transfer-Encoding: base64
Content-ID: <ii_if3zqhar0_15014363be0a41b2>
X-Attachment-Id: ii_if3zqhar0_15014363be0a41b2

iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAIAAAAlXwkiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAADFJREFUeNpi+A8BDCf/wwDD/1VIbBABIudDmAchokwgag9QAiwHVcsM
Z/5fCdYJEGAAuthJ+AVi5KgAAAAASUVORK5CYII=
--089e0158b69099488c0520cef594--

Full working raw email: Working raw-email.

However, when I send such an email via System.Net.Mail from .NET it is not working in the gmail webinterface but any other email client (outlook, iphone, etc.):

GMail webinterface with not working inline image

Relevant raw mail-header and raw-body parts of non-working email:

----boundary_3_6a0761ee-57e2-4bdd-b1f1-7302b3c8a7a1
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Image there?<br /><img src=3D"cid:[email protected]" /><=
br />Hope so!
----boundary_3_6a0761ee-57e2-4bdd-b1f1-7302b3c8a7a1--

----boundary_5_979e00c0-3fb9-46a0-b25c-1cee82cc15eb
Content-Type: image/png; name=test.png
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename=test.png
Content-ID: <[email protected]>

iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAIAAAAlXwkiAAAAGXRFWHRTb2Z0d2FyZQBB
ZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpi+A8BDCf/wwDD/1VIbBABIudDmAch
okwgag9QAiwHVcsMZ/5fCdYJEGAAuthJ+AVi5KgAAAAASUVORK5CYII=
----boundary_5_979e00c0-3fb9-46a0-b25c-1cee82cc15eb--

Full non-working raw email: Nonworking raw-email.

This is my code to send inline-images:

SmtpClient client = new SmtpClient("real.server.on.the.internet");
MailMessage mail = new MailMessage("Flattiverse <[email protected]>", "Ghostie <[email protected]>");
mail.BodyEncoding = System.Text.Encoding.UTF8;
mail.SubjectEncoding = System.Text.Encoding.UTF8;

AlternateView plainView = AlternateView.CreateAlternateViewFromString("Please view as HTML-Mail.", System.Text.Encoding.UTF8, "text/plain");
plainView.TransferEncoding = System.Net.Mime.TransferEncoding.QuotedPrintable;

AlternateView htmlView = AlternateView.CreateAlternateViewFromString("Image there?<br /><img src=\"cid:[email protected]\" /><br />Hope so!", System.Text.Encoding.UTF8, "text/html");
htmlView.TransferEncoding = System.Net.Mime.TransferEncoding.QuotedPrintable;
mail.AlternateViews.Add(plainView);
mail.AlternateViews.Add(htmlView);

mail.Subject = "7";

Attachment attachment = new Attachment("test.png", "image/png");
attachment.ContentId = "[email protected]";
attachment.ContentDisposition.Inline = true;
attachment.ContentDisposition.DispositionType = "inline; filename=test.png";
mail.Attachments.Add(attachment);

client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("working_username", "working_password");
client.Send(mail);

I also tried cid in GMail format (eg. ii_012345678_9abcdef0123456789) and many other things stated in other related questions. (Using ' instead of " in mail body, etc.)

Question: What am I doing wrong that GMail doesn't display my inline-images? How do I need to change my code? Maybe what I want can't be achieved with System.Net.Mail?

Monocycle answered 28/9, 2015 at 14:41 Comment(5)
Does it work with other email clients?Hamer
It works on the iPhone mail client and in outlook as mentioned in the question. Additionally it also works in the gmx webmailer.Monocycle
As far as I know, I created the inline images with an alternate view instead of an attachement.Hamer
@CSharper Thank you. This shows the image in GMail but not in outlook. I will see what the right solution for every mail client is and then answer my own question. If you wish, you could also answer it with a solution for regular mail clients and GMail, so i can approve your answer, if you want the reputation points.Monocycle
@Monocycle : "solution for regular mail clients ..."? There is no mechanism in the SMTP standard for inline images -- all such methods are ad hoc extensions invented by various client writers, which is one of the reasons you had the problem described in the Question.Brigand
M
11

The inline-image is ignored in GMail webinterface when added as attachment. When adding the image as alternate view it gets ignored by Outlook.

To add an inline-image compatible to GMail webinterface and Outlook (and iPhone mail client) you have to add it as LinkedResource.

The example code in the question must be fixed like this:

SmtpClient client = new SmtpClient("real.server.on.the.internet");
MailMessage mail = new MailMessage("Flattiverse <[email protected]>", "Ghostie <[email protected]>");
mail.BodyEncoding = System.Text.Encoding.UTF8;
mail.SubjectEncoding = System.Text.Encoding.UTF8;

LinkedResource image = new LinkedResource("test.png", "image/png");
image.ContentId = "[email protected]";
image.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
image.ContentType.Name = "[email protected]";
image.ContentLink = new Uri("cid:[email protected]");

AlternateView plainView = AlternateView.CreateAlternateViewFromString("Please view as HTML-Mail.", System.Text.Encoding.UTF8, "text/plain");
plainView.TransferEncoding = System.Net.Mime.TransferEncoding.QuotedPrintable;

AlternateView htmlView = AlternateView.CreateAlternateViewFromString("Image there?<br /><img src=\"cid:[email protected]\" /><br />Hope so!", System.Text.Encoding.UTF8, "text/html");
htmlView.LinkedResources.Add(image);
htmlView.TransferEncoding = System.Net.Mime.TransferEncoding.QuotedPrintable;
mail.AlternateViews.Add(plainView);
mail.AlternateViews.Add(htmlView);

mail.Subject = "15";

client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("working_username", "working_password");
client.Send(mail);
Monocycle answered 29/9, 2015 at 14:30 Comment(7)
Does it work for you now? I had working till October last year, but suddenly it stopped.Industry
This code at least worked for me. You can try to register here: www.flattiverse.com. If it sends you an email, it does still work. If not or if the images are not showing it propably isn't workin' any more.Monocycle
How download images inline from Gmail using Imap or Gmail API ?Autoeroticism
This is off-topic and therefore I don't understand your question. I used the GMail webpage, IMAP and GMail API. However, how the images are displayed is controlled by the App implementing IMAP or GMail API.Monocycle
Most underrated answer on stackoverflow. Thanks man.Oology
sad, that the OP was showing exact MIME content, but this answer is useless to anyone not using whatever API you guys are using here. Why not show the resulting MIME structure???Kemberlykemble
but how to do this for multiple images?Endearment
L
8

I had the same Problem with Pyhon (Django). Solved it by just adding the X-Attachment-Id header:

img.add_header('Content-ID', '<filename.png>')
img.add_header('X-Attachment-Id', 'filename.png')
img.add_header('Content-Disposition', 'inline', filename='filename.png')
message.attach(img)

Hope this helps someone :-)

Landscape answered 22/10, 2019 at 13:18 Comment(0)
T
5

I had the same issue (in Java, will be same for c#). Resolved by adding contentId between the “<” and “>”

enter image description here

This one is working on Gmail,yahoo and Outlook.

Ticker answered 25/2, 2020 at 21:42 Comment(5)
Thanks, this helps me after I already was deperate with showing inline images in the gmail webinterface!Livesay
The ContentID cannot contain a '<' or '>' character. Parameter name: value at System.Net.Mail.AttachmentBase.set_ContentId(String value) at ASP.research_aspx.ContentToAlternateView(String content) at ASP.research_aspx.SendMail(String subject, String body, String to)Emmeram
Wrapping the Content-ID in the <> as you have suggested definitely worked for me. It works in Thunderbird, Yahoo and Gmail web/Phone app. Also note that Gmail clients have option to "show inline images" you need to click/tap on that. You will see that option when you open the email.Klecka
the OP did that also, it wasn't the pointy brackets.Kemberlykemble
For inline images to work in the Gmail web interface, Content-ID MUST be wrapped in pointy brackets.Smell
P
1

Add it as LinkedResource.

I have a template called Confirm_Account_RegistrationInd.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Bienvenido Email de  {6}</title>
</head>

<body>
    <table width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
            <td align="center" valign="top" bgcolor="#fff" style="background-color:lightgray;">
                <br>
                <br>
                <table width="600" border="0" cellspacing="0" cellpadding="0" >
                    <tr>
                        <td height="70" align="left" valign="middle"></td>
                    </tr>
                    <tr>
                        <td align="left" valign="top" bgcolor="#564319" style="background-color:#007ebd; font-family:Arial, Helvetica, sans-serif; padding:10px;">
                            <table>
                                <tr>
                                    <td>
                                        "<img id="logo" src="miimg_id" alt="logo" style="height: 60px; border: 3px solid #007ebd; border-radius: 43px;" />";
                                    </td>
                                    <td>
                                        <div style="font-size:36px; color:#ffffff;">
                                            <b>{0}</b>
                                        </div>
                                        <div style="font-size:13px; color:lightcyan;">
                                            <b>{1} : {6}</b>
                                        </div>
                                    </td>
                                    
                                </tr>
                        </table>
                        </td>
                    </tr>
                    <tr>
                        <td align="left" valign="top" bgcolor="#ffffff" style="background-color:#ffffff;">
                            <table width="100%" border="0" cellspacing="0" cellpadding="0">
                                <tr>
                                    <td align="center" valign="middle" style="padding:10px; color:#564319; font-size:28px; font-family:Georgia, 'Times New Roman', Times, serif;">
                                        ¡Felicitaciones! <small>Estás Registrado.</small>
                                    </td>
                                </tr>
                            </table>
                            <table width="95%" border="0" align="center" cellpadding="0" cellspacing="0">
                                
                                <tr>
                                    <td width="100%" style="color:darkslategrey; font-family:Arial, Helvetica, sans-serif; padding:10px;">
                                        <div style="font-size:16px;">
                                            Apreciad@ {2},
                                        </div>
                                        <div style="font-size:12px;text-align: justify;">
                                            Ha sido creada una cuenta en el sitio.
                                            Todo lo que necesitas es hacer clic en el botón en la parte de abajo (Te tomará solamente un momento)
                                            Este correo es para la verificación de la propiedad del correo elctrónico.
                                            <hr>
                                            <center>

                                                <button type="button" title="Confirmar cuenta" style="background: darkgoldenrod">
                                                    <a href="{5}" style="font-size:22px;  padding: 10px; color: #ffffff">
                                                        Confirmar correo ahora
                                                    </a>
                                                </button>

                                            </center>
                                            <hr>
                                        </div>
                                    </td>
                                </tr>
                            </table>
                            
                            <table width="100%" border="0" align="center" cellpadding="0" cellspacing="0" style="margin-bottom:15px;">
                                <tr>
                                    <td align="left" valign="middle" style="padding:15px; font-family:Arial, Helvetica, sans-serif;">
                                        <div style="font-size:20px; color:#564319;">
                                            <b>Por favor manten tus credenciales seguras, para usarlas en el futuro. </b>
                                        </div>
                                        <div style="font-size:16px; color:#525252;">
                                            <b>Correo         :</b> {3}
                                            <br />
                                            <b>Nombre de usuario :</b> {3}
                                            <br />
                                            {7}
                                            <br />
                                        </div>
                                    </td>
                                </tr>
                            </table>
                            <table width="100%" border="0" cellspacing="0" cellpadding="0">
                                <tr>
                                    <td align="center" valign="middle" style="padding:15px; background-color:#007ebd; font-family:Arial, Helvetica, sans-serif;">

                                        <div style="font-size:20px; color:#fff;">
                                            <b>¡Actualiza tus contraseñas continuamente!</b>
                                        </div>
                                        <br>
                                        <div style="font-size:13px; color:aliceblue;">

                                            <br>


                                        </div>
                                    </td>
                                </tr>
                            </table>
                           
                        </td>
                    </tr>
                    
                </table>
                <br>
                <br>
            </td>
        </tr>
    </table>
</body>
</html>

The image that i want to show:

"<img id="logo" src="miimg_id" alt="logo" style="height: 60px; border: 3px solid #007ebd; border-radius: 43px;" />";

as you can show the src attribute has a value miimg_id

and into the view i have values to fill {}

I have my method where i will read my view as string, after get data to fill the values, called ReSendEmailAsync

[HttpPost]
        public async Task<IActionResult> ReSendEmailAsync([FromBody] string id)
        {
            string returnUrl = null;
            returnUrl = returnUrl ?? Url.Content("~/");

            string empleadoNombre = "";
            string attach = "";
            string logoName = "logo_dif.png";


            var user = await _unitOfWork.ApplicationUser.GetFirstOrDefaultAsync(u => u.Id == int.Parse(id), includeProperties: "Empleado");

            if (user == null)
            {
                return Json(new { success = false, message = "Usuario Email" });
            }
            if (user.EmailConfirmed)
            {
                return Json(new { success = true, message = "Cuenta ya fue confirmada" });
            }

            try
            {
                empleadoNombre = user.Empleado.Nombre;
            }
            catch (Exception e)
            {

            }

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user).ConfigureAwait(true);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);

            //Customizde email
            var PathToFile = _webHostEnvironment.WebRootPath + Path.DirectorySeparatorChar.ToString()
                + "Templates" + Path.DirectorySeparatorChar.ToString()
                + "EmailTemplates" + Path.DirectorySeparatorChar.ToString()
                + "Confirm_Account_RegistrationInd.html";

            var subject = "Confirmar Registro de cuenta";
            string HtmlBody = "";

            using (StreamReader streamReader = System.IO.File.OpenText(PathToFile))
            {
                HtmlBody = streamReader.ReadToEnd();
            }

        //{0} Subject
        //{1} DateTime
        //{2} Name
        //{3} Email
        //{4} Messaje
        //{5} CallBack
        //{6} AppName
        //{7} Pass

                // logo as attach
                var PathToImage = _webHostEnvironment.WebRootPath + Path.DirectorySeparatorChar.ToString()
                + "Templates" + Path.DirectorySeparatorChar.ToString()
                + "EmailTemplates" + Path.DirectorySeparatorChar.ToString()
                + logoName;

                attach = PathToImage;


            string message = $"Por favor confirme su cuenta <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>Clic Aquí</a>.";
            string messageBody = string.Format(HtmlBody,
                subject,
                String.Format("{0:dddd, d MMMM yyyy}", DateTime.Now),
                empleadoNombre,
                user.Email,
                message,
                callbackUrl,
                "Indicadores",
                ""
             );

            try
            {
                MailRequest mailRequest = new MailRequest();

                mailRequest.Body = messageBody;
                mailRequest.ToEmail = user.Email;
                mailRequest.Subject = "Confirmar su correo";
                mailRequest.Attachments = new List<MailAttachment> 
                { new MailAttachment{
                    Name = logoName,
                    Path = attach
                } };

                await _mailService.SendEmailAsync(mailRequest);
            }
            catch(Exception e)
            {
                return Json(new { success = false, message = "Al enviar email" });
            }

            return Json(new { success = true, message = "Operación exitosa" });
        }

In my class MailService

Main methed is SendEmailAsync where i pass the values to the MailMessage object

but i have another method called Mail_Body that return a AlternateView object

public class MailService : IMailService
    {
        private readonly MailSettings _mailSettings;
        public MailService(IOptions<MailSettings> mailSettings)
        {
            _mailSettings = mailSettings.Value;
        }

        public async Task SendEmailAsync(MailRequest mailRequest)
        {
            MailMessage message = new MailMessage();
            SmtpClient smtp = new SmtpClient();
            message.From = new MailAddress(_mailSettings.UserName);
            message.To.Add(new MailAddress(mailRequest.ToEmail));
            message.Subject = mailRequest.Subject;
            message.BodyEncoding = System.Text.Encoding.UTF8;
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            if (mailRequest.Attachments != null)
            {
                //int i = 0;
                foreach (var attachmentStr in mailRequest.Attachments)
                {
                    message.AlternateViews.Add(Mail_Body(mailRequest.Body, attachmentStr.Path, attachmentStr.Name));

                }
            }
            message.IsBodyHtml = true;
            smtp.Port = _mailSettings.Port;
            smtp.Host = _mailSettings.Host;
            smtp.EnableSsl = _mailSettings.EnableSSL;
            smtp.UseDefaultCredentials = false;
            smtp.Credentials = new NetworkCredential(_mailSettings.UserName, _mailSettings.Password);
            smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
            await smtp.SendMailAsync(message);
        }

        private AlternateView Mail_Body(string strr, string path, string contentId)
        {

            LinkedResource Img = new LinkedResource(path, "image/png");
            Img.ContentId = "logo_img";

            strr = strr.Replace("\"miimg_id\"", "cid:logo_img");

            AlternateView AV =
            AlternateView.CreateAlternateViewFromString(strr, null, MediaTypeNames.Text.Html);
            AV.LinkedResources.Add(Img);
            return AV;
        }
    }

the logo image is called logo_dif.png and is located in

enter image description here

finally the result in gmail:

enter image description here

Personal answered 19/8, 2020 at 16:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.