Cómo incrustar varias imágenes en el cuerpo del correo electrónico usando .NET

Estoy escribiendo un progtwig que envía correos electrónicos a usuarios con múltiples imágenes (gráficos) incrustadas en el cuerpo del mensaje de correo electrónico (HTML).

Cuando probé la muestra que se encuentra aquí … que funcionó bien cuando tengo que incrustar una sola imagen http://www.systemnetmail.com/faq/4.4.aspx .

Pero, cuando intenté incrustar varias imágenes usando el código siguiente, ninguna de las imágenes se está incrustando, sino que se envían como archivos adjuntos.

public MailMessage MailMessage(Metric metric, DateTime date) { MailMessage msg = new MailMessage(); msg.From = new MailAddress("test@gmail.com", "User1"); msg.To.Add(new MailAddress("test@gmail.com")); msg.Subject = "Trend for metric: " + metric.Name; msg.IsBodyHtml = true; // Generate the charts for the given metric var charts = this.GenerateCharts(metric, date); int i = 0; string htmlBody = ""; List resources = new List(); foreach (var chart in charts) { string imageTag = string.Format("
", i); htmlBody += imageTag; LinkedResource graph = new LinkedResource(chart.Value, "image/jpeg"); graph.ContentId = "chart" + i; resources.Add(graph); i++; } htmlBody += ""; // Alternate view for embedded images AlternateView avText = AlternateView.CreateAlternateViewFromString(metric.Name, null, MediaTypeNames.Text.Html); AlternateView avImages = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html); // Add all the images as linked resources resources.ForEach(x => avImages.LinkedResources.Add(x)); // Add the views for image msg.AlternateViews.Add(avText); msg.AlternateViews.Add(avImages); return msg; }

¿Alguna pista como lo que me estoy perdiendo? Revisé el archivo .htm que también se envía como archivo adjunto con el correo electrónico, y la fuente html se ve de la siguiente manera:

 >




Entonces, la Q es cómo enviar múltiples imágenes en el cuerpo html, no como un archivo adjunto.

La otra forma de incrustar imágenes en el correo electrónico cuando se usa System.Net.Mail es

Adjunte la imagen de la unidad local al correo electrónico y asígnele un contentID de contenido y luego use este contentID en la URL de la imagen.

Esto se puede hacer por:

 var contentID = "Image"; var inlineLogo = new Attachment(@"C:\Desktop\Image.jpg"); inlineLogo.ContentId = contentID; inlineLogo.ContentDisposition.Inline = true; inlineLogo.ContentDisposition.DispositionType = DispositionTypeNames.Inline; msg.IsBodyHtml = true; msg.Attachments.Add(inlineLogo); msg.Body = "  "; 

Entonces, creo que descubrí cuál es el problema real. Está en esta línea.

 // Alternate view for embedded images AlternateView avText = AlternateView.CreateAlternateViewFromString(metric.Name, null, MediaTypeNames.Text.Html); AlternateView avImages = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html); 

Como puede ver, mis dos vistas están especificadas como Text.Html, por lo que la primera está anulando la siguiente y solo veo el texto y las imágenes se envían como archivos adjuntos.

Hice el siguiente cambio y funcionó como se esperaba

 AlternateView avText = AlternateView.CreateAlternateViewFromString(metric.Name, null, **MediaTypeNames.Text.Plain**); AlternateView avImages = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html); 

En primer lugar , podría tratar de usar URI absolutos para imágenes incrustadas. Aquí está el ejemplo de RFC-2557 :

  From: foo1@bar.net To: foo2@bar.net Subject: A simple example Mime-Version: 1.0 Content-Type: multipart/related; boundary="boundary-example"; type="text/html"; start="" --boundary-example Content-Type: text/html;charset="US-ASCII" Content-ID:  ... text of the HTML document, which might contain a URI referencing a resource in another body part, for example through a statement such as: IETF logo --boundary-example Content-Location: http://www.ietf.cnri.restn.va.us/images/ietflogo.gif Content-Type: IMAGE/GIF Content-Transfer-Encoding: BASE64 R0lGODlhGAGgAPEAAP/////ZRaCgoAAAACH+PUNvcHlyaWdodCAoQykgMTk5 NSBJRVRGLiBVbmF1dGhvcml6ZWQgZHVwbGljYXRpb24gcHJvaGliaXRlZC4A etc... --boundary-example-- 

Solo debe asignar la propiedad LinkedResource.ContentLink en lugar de ContentId.

En segundo lugar , puede incrustar imágenes directamente en su html con el esquema de URL de “datos” .

  Larry 

Por cierto, su marcado html no está bien formado. Usted también podría estar interesado en “foreach” vs “ForEach”

si tiene las imágenes en línea, es decir, enviando desde un sitio alojado, le sugiero que solo haga referencia a esas imágenes simplemente colocando su url en el src.

  IETF logo 

la mayoría de los boletines utilizan este método, y creo que es más liviano y puede consumir menos recursos que su propia incrustación.

espero que esto ayude

Mi alternativa:

Antes que nada, una pequeña extensión:

 public static class RegexExtensions { public static string GetPattern(this IEnumerable valuesToSearch) { return string.Format("({0})", string.Join("|", valuesToSearch)); } } 

luego obtenga los nombres de las imágenes de la carpeta:

  private string[] GetFullNamesOfImages() { string images = Path.Combine(_directoryName, "Images"); if (!Directory.Exists(images)) return new string[0]; return Directory.GetFiles(images); } 

luego reemplazando los nombres de las imágenes por cid:

  private string InsertImages(string body) { var images = GetFullNamesOfImages().Select(Path.GetFileName).ToArray(); return Regex.Replace(body, "(Images/)?" + images.GetPattern(), "cid:$2", RegexOptions.IgnoreCase | RegexOptions.Compiled); } 

donde cuerpo – es cuerpo HTML, y por ejemplo, será reemplazado por

luego, la última acción: agregar las imágenes al correo:

  private MailMessage CreateMail(SmtpClient smtp, string toAddress, string body) { var images = GetFullNamesOfImages(); string decodedBody = WebUtility.HtmlDecode(body); var text = AlternateView.CreateAlternateViewFromString(decodedBody, null, MediaTypeNames.Text.Plain); var html = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html); foreach (var image in images) { html.LinkedResources.Add(new LinkedResource(image, new ContentType("image/png")) { ContentId = Path.GetFileName(image) }); } var credentials = (NetworkCredential) smtp.Credentials; var message = new MailMessage(new MailAddress(credentials.UserName), new MailAddress(toAddress)) { Subject = "Some subj", Body = decodedBody }; message.AlternateViews.Add(text); message.AlternateViews.Add(html); return message; } 
  AlternateView avHtml = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html); LinkedResource inline = new LinkedResource(System.Web.HttpContext.Current.Server.MapPath("~/Images/e1.jpg"), MediaTypeNames.Image.Jpeg); inline.ContentId = "1"; inline.TransferEncoding = System.Net.Mime.TransferEncoding.Base64; avHtml.LinkedResources.Add(inline); LinkedResource inline1 = new LinkedResource(System.Web.HttpContext.Current.Server.MapPath("~/CImages/2.jpg"), MediaTypeNames.Image.Jpeg); inline1.ContentId = "2"; inline1.TransferEncoding = System.Net.Mime.TransferEncoding.Base64; avHtml.LinkedResources.Add(inline1); LinkedResource inline2 = new LinkedResource(System.Web.HttpContext.Current.Server.MapPath("~/Images/3.jpg"), MediaTypeNames.Image.Jpeg); inline2.ContentId = "3"; inline2.TransferEncoding = System.Net.Mime.TransferEncoding.Base64; avHtml.LinkedResources.Add(inline2); LinkedResource inline3 = new LinkedResource(System.Web.HttpContext.Current.Server.MapPath("~/Content/Images/4.jpg"), MediaTypeNames.Image.Jpeg); inline3.ContentId = "4"; inline3.TransferEncoding = System.Net.Mime.TransferEncoding.Base64; avHtml.LinkedResources.Add(inline3); MailMessage mail = new MailMessage(); mail.AlternateViews.Add(avHtml); 

HTML: