Search code examples
azureazure-web-app-servicesendgridmailmessage

Email Attachments missing on Azure, works locally


I have implemented something similar to the following question and I can get it working locally on my server, but when I deploy to Azure it doesn't work. I don't get any errors: just an email without the attachment.

Sending attachments using Azure blobs

Are there restrictions on what types of files can be sent with SendGrid (file is only 56k)? Does the Azure App service have to be at a particular level or can it be done on Basic? The blob URL definitley exists and I am setting the stream to zero as suggested in that previous question.

MailMessage mm = new MailMessage("receiver address", "someone");
            mm.From = new MailAddress("myAddress", "My Name");
            mm.Subject = content.Subject;
            mm.Body = content.Body;
            mm.IsBodyHtml = true;
            mm.BodyEncoding = UTF8Encoding.UTF8;
            mm.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;

            var attachmentAsStream = _storageAccessService.GetAssetAsStreamForEmail("myContainer", "fileThatExists.pdf");

            var attachment = new Attachment(attachmentAsStream, "File.pdf", MediaTypeNames.Application.Pdf);
                mm.Attachments.Add(attachment);


public MemoryStream GetAssetAsStreamForEmail(string containerName, string fileName)
        {
            // Create the blob client.
            CloudBlobClient blobClient = StorageAccountReference().CreateCloudBlobClient();

            // Retrieve reference to a previously created container.
            CloudBlobContainer container = blobClient.GetContainerReference(containerName);

            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
            var memoryStream = new MemoryStream();

            try
            {
                using (var stream = new MemoryStream())
                {
                    blob.DownloadToStream(memoryStream);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                }

            }
            catch (Exception ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Failed to download Email Atatchment: "+ ex.Message));

            }
            memoryStream.Position = 0;
            return memoryStream;
        }

using (SmtpClient client = new SmtpClient())
                {
                    client.Port = 587;
                    client.Host = "smtp.sendgrid.net";
                    client.EnableSsl = true;
                    client.Timeout = 10000;
                    client.DeliveryMethod = SmtpDeliveryMethod.Network;
                    client.UseDefaultCredentials = false;
                    client.Credentials = new System.Net.NetworkCredential("hidden", "hidden");

                    await client.SendMailAsync(message);
                }

Update

Update after Yasir's suggestion below. Downloading the blob from Azure as a Stream only seems to work locally. But if I change to download as a ByteArray then it works everywhere, nonetheless...

public MemoryStream GetAssetAsStreamForEmail(string containerName, string fileName)
        {
            // Create the blob client.
            CloudBlobClient blobClient = StorageAccountReference().CreateCloudBlobClient();

            // Retrieve reference to a previously created container.
            CloudBlobContainer container = blobClient.GetContainerReference(containerName);

            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

            try
            {
                blob.FetchAttributes();
                var fileStream = new byte[blob.Properties.Length];
                for (int i = 0; i < blob.Properties.Length; i++)
                {
                    fileStream[i] = 0x20;
                }

                blob.DownloadToByteArray(fileStream, 0) ;
                MemoryStream bufferStream = new MemoryStream(fileStream);

            }
            catch (Exception ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Failed to download Email Atatchment: " + ex.Message));

            }
            return null;
        }

Solution

  • The following code works for me for sending emails using Sendgrid from Azure functions. I attach a CSV file from Blob Storage. It should work for you as well. All you will have to do is ensure that you read the pdf file as a byte[].

        public interface IEmailAttachment
        {
            string Name { get; }
            byte[] FileData { get; }
        }
    
        public static void Send(MailMessage mailMessage, IEnumerable<IEmailAttachment> attachments)
        {
        try
        {
            // Get the configuration data
            string server = ConfigReader.EmailServer;
            int port = ConfigReader.EmailPort;
            string username = ConfigReader.SendGridUserName;
            string password = ConfigReader.SendGridPassword;
            smtpClient.EnableSsl = false;
            smtpClient.Credentials = new NetworkCredential(username, password);
    
            // Create the SMTP Client
            SmtpClient smtpClient = new SmtpClient(server, port);
    
            // Prepare the MailMessage
            mailMessage.From = new MailAddress(ConfigReader.FromEmail);
    
            var toEmails = ConfigReader.ToEmail.Split(',');
    
            foreach (var toEmail in toEmails)
            {
                mailMessage.To.Add(toEmail);
            }
    
            var ccEmails = ConfigReader.EmailCc.Split(',');
    
            foreach (var ccEmail in ccEmails)
            {
                mailMessage.CC.Add(ccEmail);
            }
    
    
            // Add attachments
            List<MemoryStream> files = new List<MemoryStream>();
    
            if (attachments != null)
            {
                foreach (IEmailAttachment file in attachments)
                {
                    MemoryStream bufferStream = new MemoryStream(file.FileData);
                    files.Add(bufferStream);
    
                    Attachment attachment = new Attachment(bufferStream, file.Name);
                    mailMessage.Attachments.Add(attachment);
                }
            }
    
            mailMessage.IsBodyHtml = true;
    
            // Send the email
            smtpClient.Send(mailMessage);
    
            foreach (MemoryStream stream in files)
            {
                stream.Dispose();
            }
        }
        catch (Exception)
        {
            throw;
        }
    }