Skip to content

Instantly share code, notes, and snippets.

@idusortus
Last active April 7, 2021 02:14
Show Gist options
  • Save idusortus/337ef7148b4d78495bad232cbcea5b62 to your computer and use it in GitHub Desktop.
Save idusortus/337ef7148b4d78495bad232cbcea5b62 to your computer and use it in GitHub Desktop.
Generate SendGrid Emails with Inline Images using C# .NET Core 3.1 ( Console )

Not optimized, or clean, but gets the point across. Getting inline images to show in the email was obnoxious. Key solution points are provided.

Backing Method

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SendGrid.SmtpApi;
using SendGrid;
using SendGrid.Helpers.Mail;
using Microsoft.Azure.WebJobs;

public static async Task<IActionResult> TestEmailViaSendGridAPI(ComplexEmailModel cem, string sendGridApiKey)
{
    try
    {
        // Credentials
        var client = new SendGridClient(sendGridApiKey);
        // Email Basics
        var from = new EmailAddress(cem.From);// needs to be whitelisted with service provider
        var to = new EmailAddress(cem.To);
        var subject = cem.Subject;
        // Default content - Should only be visible to luddites and in case of an issue rendering the HTML            
        var plainTextContent = cem.Content;
        // This content should have cid placeholders for each image to be added to the email
        var htmlContent = cem.TemplateBody;
        // build base SendGridMessage 
        var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
        // replace cid:'s with images
        foreach (var att in cem.InlineAttachments)
            msg.AddAttachment(att);
        // send email & report results
        var response = await client.SendEmailAsync(msg);
        if (response.IsSuccessStatusCode) 
            return new OkObjectResult(response);
        else 
            return new BadRequestObjectResult(response);
    }
    catch (Exception wtf)
    {
        return new BadRequestObjectResult(wtf.Message);
    }
}

Example Model

public class ComplexEmailModel 
{
    [JsonProperty("from")]
    public string From { get; set; } = "no-reply@matchhub.us";

    [JsonProperty("to")]
    public string To { get; set; }

    [JsonProperty("subject")]
    public string Subject { get; set; } = "Notice";

    [JsonProperty("content")]
    public string Content { get; set; } = $"A status update is available for review on MatchHub.";  

    [JsonProperty("templateBody")]
    public string TemplateBody { get; set; }

    [JsonProperty("inlineImages")]
    public List<SendGrid.Helpers.Mail.Attachment> InlineAttachments { get; set; }
}

Async Main()

namespace LocalEmailTest
{
class Program
{
    private const string SendGrid_FreeKey = "INSERT-YOUR-API-KEY-HERE";
    
    static async System.Threading.Tasks.Task Main(string[] args)
    {
        // retrieve the email template.
        // In this example, there should be two image tags with the following params:
        // <img src="cid:image1" ...
        // <img src="cid:image2" ...
        string templateName = "Template1.html";
        string templateFullPath = System.IO.Path.Combine(Environment.CurrentDirectory, templateName);
        System.IO.StreamReader str = new System.IO.StreamReader(templateFullPath);
        string mailBodyContent = str.ReadToEnd();
        str.Close();
        // Convert image one to a b64 encoded string.
        string imageFileName1 = "image-1.png";
        string inlineImagePath1 = System.IO.Path.Combine(Environment.CurrentDirectory, imageFileName1);
        var b64_1 = Convert.ToBase64String(System.IO.File.ReadAllBytes(inlineImagePath1));
        // Convert image two to a b64 encoded string.
        string imageFileName2 = "image-2.gif";
        string inlineImagePath2 = System.IO.Path.Combine(Environment.CurrentDirectory, imageFileName2);
        var b64_2 = Convert.ToBase64String(System.IO.File.ReadAllBytes(inlineImagePath2));
        //// uncomment to verify paths
        //Console.WriteLine(path);
        //Console.WriteLine(inlineImagePath1);
        //Console.WriteLine(inlineImagePath2);       
	
	// instantiate Attachment object for each unique image.
        SendGrid.Helpers.Mail.Attachment logoImage = new SendGrid.Helpers.Mail.Attachment
        {
            ContentId = "image1",
            Content = b64_1,
            Type = "image/png",
            Disposition = "inline",
            Filename = "image-1.png"
        };
        SendGrid.Helpers.Mail.Attachment statusImage = new SendGrid.Helpers.Mail.Attachment
        {
            ContentId = "image2",
            Content = b64_2,
            Type = "image/gif",
            Disposition = "inline",
            Filename = "image-2.gif"
        };
	// collect Attachments
        var imageAttachments = new List<SendGrid.Helpers.Mail.Attachment> { logoImage, statusImage };
	// pack info for transmission
        var cem = new ComplexEmailModel
        {
            To = "recipient@gmail.com",
            Subject = "Look, Ma, Images!",
            TemplateBody = mailBodyContent,
            InlineAttachments = imageAttachments                
        };

        var emailResp = await SendGridAPIMailService.TestEmailViaSendGridAPI(cem, SendGrid_FreeKey);
        Console.WriteLine(emailResp);
    }
}
}

.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="SendGrid" Version="9.22.0" />
		<PackageReference Include="SendGrid.SmtpApi" Version="1.3.9" />
	</ItemGroup>
	<ItemGroup>
		<None Update="Template1.html">
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</None>
		<None Update="image-1.png">
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</None>
		<None Update="image-2.gif">
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</None>
	</ItemGroup>
</Project>

Template.html (snip of an well-formed html page - check out https://dashboard.unlayer.com/ for ideas)

<body><table><tbody><tr>
	<img src="cid:image1" /><img src="cid:image2" />
</tr></tbody></table></body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment