使用SmtpClient发送内嵌图片的C#邮件

40

SmtpClient()允许您向邮件中添加附件,但如果您想在邮件打开时使图像出现,而不是将其作为附件添加,该怎么办呢?

据我记得,只需要大约4行代码就可以完成,但我不记得具体细节了,也找不到MSDN网站上的相关信息。

编辑:我没有使用任何网站或IP地址。这些图片位于硬盘上。发送时,它们应该成为邮件的一部分。因此,我想我可能需要使用一个 tag...但我不太确定,因为我的电脑没有广播。


@JamesMcCormack 我可能错过了那个时候的所有内容。已更新。 - KdgDev
请查看此链接。它提供了多个内联附件的使用方法,包括png文件流和pdf/excel文件流的一般附件。https://stackoverflow.com/questions/33665280/add-multiple-images-in-the-email-body-inlineusing-c-sharp-windows-application/49329461#49329461 - kumar chandraketu
6个回答

87

经常提到的解决方案是将图像作为附件添加到邮件中,然后在HTML邮件正文中使用cid:引用来引用它。

但是,如果您改用LinkedResources集合,则内联图像仍将显示得很好,但不会作为邮件的其他附件显示。 这正是我们想要发生的事情,所以我在这里这样做:

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);
}

注意: 这个解决方案向你的 MailMessage 添加了一个类型为 text/htmlAlternateView。为了完整起见,你还应该添加一个类型为 text/plainAlternateView,其中包含邮件的纯文本版本,以供非 HTML 邮件客户端使用。


1
最佳实践是在电子邮件中包含2个AlternateViews,第一个为“text/plain”类型,包含纯文本邮件正文。第二个View将是我上面详细介绍的“text/html”视图。 - James McCormack
1
你是一个救星。经过两天的搜索...哦 - Ehsan Zargar Ershadi
2
这是一个很好的解决方案。谢谢您加入它。我还建议添加如何使用Image元素完成此操作。我使用LinkedResource和MemoryStream来实现了这一点。一旦我在获取流后重置了.Position属性,就能很好地工作! - Matthew M.
2
Server.MapPath -> "Server" 是在哪里定义的? - ThunderGr
4
为了让这个工作起来,我需要在创建"image/png"的LinkedResource时包含MIME内容。我使用的是.Net 4.5。 - ThunderGr
显示剩余9条评论

13

你所说的4行代码是指这个吗?

System.Net.Mail.Attachment inline = new System.Net.Mail.Attachment(@"imagepath\filename.png");
inline.ContentDisposition.Inline = true;

你的链接已经失效了吗? - Maytham Fahmi

13

HTML邮件和图片都是附件,所以只需通过它们的内容ID来引用图像,即:

    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 +"'>" 

这里似乎有全面的示例:使用内联图像发送电子邮件


这个方法是有效的,图片确实出现在邮件正文的正确位置,但我猜测楼主不希望图片也出现在附件列表中。我认为正确的答案是使用LinkedResource - 请参考我的回答。 - James McCormack
什么是cid?我们需要在MailMessage对象上设置一些设置才能使用MIME吗? 另外,为什么您要在100000到9999999之间使用随机数作为内容ID?我正在PowerShell中使用您的代码,但图像显示为红色十字。 - David Klempfner
当您需要唯一标识时,不应使用随机数生成器。请使用GUID或其他保证唯一的方法。 - Dan Bechard
它不需要全局唯一,但最终只是品味问题。这只是一个示例,不适用于生产环境代码。 - Lazarus
如果它的内容设置为“inline”而不是“attachment”,它将不会显示在邮件客户端的附件中(在Outlook中不会显示)。 - Sam
Lazarus,非常感谢您。我已经试了好几天来解决这个问题,而我所缺少的只是对contentid的引用。这个方法非常有效。 - vbNewbie

2

将图像转换为Base64字符串怎么样?据我所知,这可以轻松地嵌入电子邮件正文中。

请看这里


我仍然坚信这应该被视为一种选项。http://www.sweeting.org/mark/blog/2005/07/12/base64-encoded-images-embedded-in-html - ccalboni
图片将被base64编码,这与在HTML电子邮件中显示它无关。-1不是我发的,但答案有点偏离问题。 - Lazarus
我想说的是,如果你想在HTML文档中嵌入一张图片,你可以这样做。只需将其编码为base64,并将结果字符串放置在img标签的src属性中即可。在这里看看:http://www.tekasarts.com/stuff/base_64_img.html我的做法是:
  • 在Photoshop中渐变填充gif
  • Convert.ToBase64String() 图像字节
  • 粘贴到HTML文档中
据我所知,这可能不是首选解决方案,但它是相关的,并且在某种程度上是一个相当好的解决方案。
- ccalboni
为什么这个被投票否决了?它是正确的。你可以在数据URL中嵌入图片。 - usr
关于Base64字符串中的图像,我认为有一些限制(例如大小)。 - Żubrówka
4
这个问题是关于电子邮件,而不是HTML。许多流行的邮件客户端(如所有版本的Outlook)无法显示Base64编码的图片。 - Dan Bechard

0

已经发布的解决方案是我找到的最好的,我只是要补充一下,例如如果您有多个图像。

        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);
                }
            }
        });

正如你所看到的,你有一个图像名称的数组,重要的是这些图像都在同一个文件夹中,因为它们指向相同的输出文件夹。

最后,发送电子邮件是异步的,因此用户无需等待它被发送。


-2
当邮件被打开时,使图像在客户端上显示的过程是客户端功能。只要客户端知道如何呈现图像并且没有阻止任何图像内容,它就会立即打开它。只要您正确指定了图像MIME附件类型,发送电子邮件时无需进行任何特殊操作即可在客户端上打开它。

1
请再次阅读问题。他想要附加图片并在正文中显示。 - Thomas Freudenberg
这个最好作为对被接受答案的评论。 - Alan B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接