Search code examples
asp.net-mvcasp.net-mvc-3memorystreammvcmailer

Using MVCMailer to embed image from MemoryStream in email


I use MVCMailer with asp.net MVC 3. It's a great library but I have a problem.

I saw it's possible to embed image in email like so :

var resources = new Dictionary<string, string>();
resources["image"] = imagePath;
PopulateBody(mailMessage, "WelcomeMessage", resources);

Therefore it looks like "resources" is expecting a path to the image from the filesystem, however, my image is in memorystream.

Would it be possible to embed the image as a base64 straight away without having to actually write the file on disk and then pass the path?

Thanks for your help!


Solution

  • Baucause MVCMailer is based on System.Net.Mail it's easy to add LinkedResource as stream.

    Here is the fix :

    in ILinkedResourceProvider.cs add :

    List<LinkedResource> GetAll(Dictionary<string, MemoryStream> resources);
    LinkedResource Get(string contentId, MemoryStream stream);
    

    in LinkedResourceProvider add :

    public virtual List<LinkedResource> GetAll(Dictionary<string, MemoryStream> resources)
    {
        var linkedResources = new List<LinkedResource>();
        foreach (var resource in resources)
        {
            linkedResources.Add(Get(resource.Key, resource.Value));
        }
        return linkedResources;
    }
    public virtual LinkedResource Get(string contentId, MemoryStream stream)
    {
        LinkedResource resource = new LinkedResource(stream);
        resource.ContentId = contentId;
        return resource;
    }
    

    In MailerBase.cs add :

    public virtual void PopulateBody(MailMessage mailMessage, string viewName, Dictionary<string, MemoryStream> linkedResources)
    {
        PopulateBody(mailMessage, viewName, null, linkedResources);
    }
    public virtual void PopulateBody(MailMessage mailMessage, string viewName, string masterName = null, Dictionary<string, MemoryStream> linkedResources = null)
    {
        if (mailMessage == null)
        {
            throw new ArgumentNullException("mailMessage", "mailMessage cannot be null");
        }
    
        masterName = masterName ?? MasterName;
    
        var linkedResourcesPresent = linkedResources != null && linkedResources.Count > 0;
        var textExists = TextViewExists(viewName, masterName);
    
        //if Text exists, it always goes to the body
        if (textExists)
        {
            PopulateTextBody(mailMessage, viewName, masterName);
        }
    
        // if html exists
        if (HtmlViewExists(viewName, masterName))
        {
            if (textExists || linkedResourcesPresent)
            {
                PopulateHtmlPart(mailMessage, viewName, masterName, linkedResources);
            }
            else
            {
                PopulateHtmlBody(mailMessage, viewName, masterName);
            }
        }
    }
    public virtual AlternateView PopulateHtmlPart(MailMessage mailMessage, string viewName, string masterName, Dictionary<string, MemoryStream> linkedResources)
    {
        var htmlPart = PopulatePart(mailMessage, viewName, "text/html", masterName);
        if (htmlPart != null)
        {
            PopulateLinkedResources(htmlPart, linkedResources);
        }
        return htmlPart;
    }
    public virtual List<LinkedResource> PopulateLinkedResources(AlternateView mailPart, Dictionary<string, MemoryStream> resources)
    {
        if (resources == null || resources.Count == 0)
            return new List<LinkedResource>();
    
        var linkedResources = LinkedResourceProvider.GetAll(resources);
        linkedResources.ForEach(resource => mailPart.LinkedResources.Add(resource));
        return linkedResources;
    }
    

    Hope it will be part of next MVCMailer release.