Zain Ali
Zain Ali

Reputation: 15993

Attach a file from MemoryStream to a MailMessage in C#

I am writing a program to attach a file to email. Currently I am saving file using FileStream into disk, and then I use

System.Net.Mail.MailMessage.Attachments.Add(
    new System.Net.Mail.Attachment("file name")); 

I do not want to store file in disk, I want to store file in memory and from memory stream pass this to Attachment.

Upvotes: 136

Views: 221784

Answers (9)

Martin Alex Okello
Martin Alex Okello

Reputation: 1

I am assuming that when you dispose the MailMessage, the Memory Steams get disposed too, as mentioned above by one of you chaps. A late entry, and was having memory stream exception of stream too long. Of course then I wasn't disposing my mailmessage. So this may help, that I am sending multi-file Attachments:

    public void SendEmail(EmailDao mail)
    {
        try
        {
            //Send Email:
            var networkCredentials = new NetworkCredential { UserName = _businessSmtpDetails.GetSection("NetworkUsername").Value, Password = _businessSmtpDetails.GetSection("NetworkPassword").Value };
            var smtpServer = new SmtpClient(_businessSmtpDetails.GetSection("SmtpServer").Value);
            smtpServer.Credentials = networkCredentials;

            var mailMessage = new MailMessage();

            mailMessage.From = new MailAddress(_businessSmtpDetails.GetSection("BusinessEmail").Value);
            mailMessage.Body = mail.EmailBody;
            mailMessage.Subject = @"From " + mail.EmailFrom + " " + mail.EmailSubject;
            var fileStream = new FileInfo("/images/attachement");
            MemoryStream memoryStream = null;
            if (mail.Attachment != null)
            {
                memoryStream = new MemoryStream();
                ReadFileAttachment(mail.Attachment, memoryStream);
                memoryStream.Seek(0, SeekOrigin.Begin);
                System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
                var attached = new Attachment(memoryStream, mail.Attachment.FileName, ct.MediaType);
                mailMessage.Attachments.Add(attached);
            }
            if (mail.Attachments != null && mail.Attachments.Length > 0)
            {
                foreach (var attachment in mail.Attachments)
                {
                    memoryStream = new MemoryStream();
                    ReadFileAttachment(attachment, memoryStream);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
                    var attached = new Attachment(memoryStream, mail.Attachment.FileName, ct.MediaType);
                    mailMessage.Attachments.Add(attached);
                }
            }
            mail.EmailTo += string.Format(";{0}", _businessSmtpDetails.GetSection("BusinessEmail").Value);
            Array.ForEach<string>(mail.EmailTo.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries), (p) =>
            {
                mailMessage.To.Add(p);
            });
            smtpServer.Send(mailMessage);
            memoryStream.Close();
            mailMessage.Dispose();
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    private MemoryStream ReadFileAttachment(IFormFile attacment, MemoryStream memoryStream)
    {
        var bytes = new byte[4096];
        var bytesRead = 0;
        while ((bytesRead = attacment.OpenReadStream().Read(bytes, 0, bytes.Length)) > 0)
        {
            memoryStream.Write(bytes, 0, bytesRead);
            memoryStream.Flush();
        }
        return memoryStream;
    }

Upvotes: 0

tranquil tarn
tranquil tarn

Reputation: 1756

A bit of a late entry - but hopefully still useful to someone out there:-

Here's a simplified snippet for sending an in-memory string as an email attachment (a CSV file in this particular case).

using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))    // using UTF-8 encoding by default
using (var mailClient = new SmtpClient("localhost", 25))
using (var message = new MailMessage("[email protected]", "[email protected]", "Just testing", "See attachment..."))
{
    writer.WriteLine("Comma,Separated,Values,...");
    writer.Flush();
    stream.Position = 0;     // read from the start of what was written
    
    message.Attachments.Add(new Attachment(stream, "filename.csv", "text/csv"));
    
    mailClient.Send(message);
}

The StreamWriter and underlying stream must not be disposed until after the message has been sent (to avoid ObjectDisposedException: Cannot access a closed Stream).

Upvotes: 108

Jason Dimmick
Jason Dimmick

Reputation: 500

If you actually want to add a .pdf, I found it necessary to set the position of the memory stream to Zero.

var memStream = new MemoryStream(yourPdfByteArray);
memStream.Position = 0;
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Application.Pdf);
var reportAttachment = new Attachment(memStream, contentType);
reportAttachment.ContentDisposition.FileName = "yourFileName.pdf";
mailMessage.Attachments.Add(reportAttachment);

Upvotes: 30

Waqas Raja
Waqas Raja

Reputation: 10870

Here is the sample code.

System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.StreamWriter writer = new System.IO.StreamWriter(ms);
writer.Write("Hello its my sample file");
writer.Flush();
writer.Dispose();
ms.Position = 0;

System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(ms, ct);
attach.ContentDisposition.FileName = "myFile.txt";

// I guess you know how to send email with an attachment
// after sending email
ms.Close();

Edit 1

You can specify other file types by System.Net.Mime.MimeTypeNames like System.Net.Mime.MediaTypeNames.Application.Pdf

Based on Mime Type you need to specify correct extension in FileName for instance "myFile.pdf"

Upvotes: 129

use OTHER OPEN memorystream:

example for lauch pdf and send pdf in MVC4 C# Controller

        public void ToPdf(string uco, int idAudit)
    {
        Response.Clear();
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("content-disposition", "attachment;filename= Document.pdf");
        Response.Buffer = true;
        Response.Clear();

        //get the memorystream pdf
        var bytes = new MisAuditoriasLogic().ToPdf(uco, idAudit).ToArray();

        Response.OutputStream.Write(bytes, 0, bytes.Length);
        Response.OutputStream.Flush();

    }


    public ActionResult ToMail(string uco, string filter, int? page, int idAudit, int? full) 
    {
        //get the memorystream pdf
        var bytes = new MisAuditoriasLogic().ToPdf(uco, idAudit).ToArray();

        using (var stream = new MemoryStream(bytes))
        using (var mailClient = new SmtpClient("**YOUR SERVER**", 25))
        using (var message = new MailMessage("**SENDER**", "**RECEIVER**", "Just testing", "See attachment..."))
        {

            stream.Position = 0;

            Attachment attach = new Attachment(stream, new System.Net.Mime.ContentType("application/pdf"));
            attach.ContentDisposition.FileName = "test.pdf";

            message.Attachments.Add(attach);

            mailClient.Send(message);
        }

        ViewBag.errMsg = "Documento enviado.";

        return Index(uco, filter, page, idAudit, full);
    }

Upvotes: 2

ilForna
ilForna

Reputation: 51

I landed on this question because I needed to attach an Excel file I generate through code and is available as MemoryStream. I could attach it to the mail message but it was sent as 64Bytes file instead of a ~6KB as it was meant. So, the solution that worked for me was this:

MailMessage mailMessage = new MailMessage();
Attachment attachment = new Attachment(myMemorySteam, new ContentType(MediaTypeNames.Application.Octet));

attachment.ContentDisposition.FileName = "myFile.xlsx";
attachment.ContentDisposition.Size = attachment.Length;

mailMessage.Attachments.Add(attachment);

Setting the value of attachment.ContentDisposition.Size let me send messages with the correct size of attachment.

Upvotes: 3

MikeTeeVee
MikeTeeVee

Reputation: 19422

If all you're doing is attaching a string, you could do it in just 2 lines:

mail.Attachments.Add(Attachment.CreateAttachmentFromString("1,2,3", "text/csv");
mail.Attachments.Last().ContentDisposition.FileName = "filename.csv";

I wasn't able to get mine to work using our mail server with StreamWriter.
I think maybe because with StreamWriter you're missing a lot of file property information and maybe our server didn't like what was missing.
With Attachment.CreateAttachmentFromString() it created everything I needed and works great!

Otherwise, I'd suggest taking your file that is in memory and opening it using MemoryStream(byte[]), and skipping the StreamWriter all together.

Upvotes: 14

r12
r12

Reputation: 59

I think this code will help you:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Mail;

public partial class _Default : System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {

  }

  protected void btnSubmit_Click(object sender, EventArgs e)
  {
    try
    {
      MailAddress SendFrom = new MailAddress(txtFrom.Text);
      MailAddress SendTo = new MailAddress(txtTo.Text);

      MailMessage MyMessage = new MailMessage(SendFrom, SendTo);

      MyMessage.Subject = txtSubject.Text;
      MyMessage.Body = txtBody.Text;

      Attachment attachFile = new Attachment(txtAttachmentPath.Text);
      MyMessage.Attachments.Add(attachFile);

      SmtpClient emailClient = new SmtpClient(txtSMTPServer.Text);
      emailClient.Send(MyMessage);

      litStatus.Text = "Message Sent";
    }
    catch (Exception ex)
    {
      litStatus.Text = ex.ToString();
    }
  }
}

Upvotes: -8

Thymine
Thymine

Reputation: 9205

Since I couldn't find confirmation of this anywhere, I tested if disposing of the MailMessage and/or the Attachment object would dispose of the stream loaded into them as I expected would happen.

It does appear with the following test that when the MailMessage is disposed, all streams used to create attachments will also be disposed. So as long as you dispose your MailMessage the streams that went into creating it do not need handling beyond that.

MailMessage mail = new MailMessage();
//Create a MemoryStream from a file for this test
MemoryStream ms = new MemoryStream(File.ReadAllBytes(@"C:\temp\test.gif"));

mail.Attachments.Add(new System.Net.Mail.Attachment(ms, "test.gif"));
if (mail.Attachments[0].ContentStream == ms) Console.WriteLine("Streams are referencing the same resource");
Console.WriteLine("Stream length: " + mail.Attachments[0].ContentStream.Length);

//Dispose the mail as you should after sending the email
mail.Dispose();
//--Or you can dispose the attachment itself
//mm.Attachments[0].Dispose();

Console.WriteLine("This will throw a 'Cannot access a closed Stream.' exception: " + ms.Length);

Upvotes: 33

Related Questions