Adjunte un archivo de MemoryStream a un MailMessage en C #

113

Estoy escribiendo un programa para adjuntar un archivo al correo electrónico. Actualmente estoy guardando el archivo usando FileStreamen el disco, y luego uso

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

No quiero almacenar el archivo en el disco, quiero almacenar el archivo en la memoria y desde el flujo de memoria pasar esto a Attachment.

Zain Ali
fuente

Respuestas:

104

Aquí está el código de ejemplo.

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();

Editar 1

Puede especificar otros tipos de archivos por System.Net.Mime.MimeTypeNames como System.Net.Mime.MediaTypeNames.Application.Pdf

Según el tipo de Mime , debe especificar la extensión correcta en FileName, por ejemplo"myFile.pdf"

Waqas Raja
fuente
Estoy usando PDF Cómo pasar este tipo a System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType (System.Net.Mime.MediaTypeNames.Text.Plain);
Zain Ali
3
Necesita usarSystem.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja
5
writer.Disopose()era demasiado pronto para mi solución, pero todo lo demás es un gran ejemplo.
Kazimierz Jawor
7
Estoy bastante seguro de que debería haber un ms.Position = 0;antes de crear el archivo adjunto.
Kenny Evitt
94

Una entrada un poco tardía, pero con suerte todavía será útil para alguien:

Aquí hay un fragmento simplificado para enviar una cadena en memoria como un archivo adjunto de correo electrónico (un archivo CSV en este caso particular).

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,Seperated,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);
}

El StreamWriter y el flujo subyacente no deben eliminarse hasta que se haya enviado el mensaje (para evitarlo ObjectDisposedException: Cannot access a closed Stream).

tranquilo tarn
fuente
30
Para cualquier recién llegado, la clave para mí fue establecer elstream.position = 0;
mtbennett
3
+1 para el uso apropiado de using (), algo que siempre parece faltar en los ejemplos y fragmentos en línea (incluida la respuesta aceptada a esta pregunta).
Jay Querido
2
Gracias a @mtbennet, ese también fue mi problema: la configuración stream.Position=0.
Ícaro
¿Qué hace realmente la configuración del tipo MIME aquí? ¿Algo para la aplicación cliente de correo electrónico?
xr280xr
@ xr280xr - Correcto. Este parámetro no es realmente necesario, pero incluirlo debería ayudar al cliente de correo electrónico del destinatario a manejar el archivo adjunto con sensatez. docs.microsoft.com/en-us/dotnet/api/…
tranquilo tarn
28

Como no pude encontrar la confirmación de esto en ninguna parte, probé si deshacerse del MailMessage y / o el objeto Attachment eliminaría la secuencia cargada en ellos como esperaba.

Parece con la siguiente prueba que cuando se elimina el mensaje de correo, también se eliminarán todas las secuencias utilizadas para crear archivos adjuntos. Por lo tanto, siempre que elimine su MailMessage, las secuencias que se utilizaron para crearlo no necesitan un manejo más allá de eso.

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);
Timina
fuente
Maldita sea, eso es inteligente. Una línea de código y podría agregar un archivo de imagen a un correo electrónico como archivo adjunto. ¡Buen consejo!
Mike Gledhill
Ojalá esto estuviera realmente documentado. Su prueba / verificación de este comportamiento es útil, pero sin estar en la documentación oficial es difícil confiar en que siempre será así. De todos modos, gracias por probar.
Lucas
Buen esfuerzo e investigación @thymine
vibs2006
1
está algo documentado, si la fuente de referencia cuenta. mailmessage.dispose llama adjuntos.dispose que a su vez las llamadas disponen en cada adjunto que, en mimepart, cierra el flujo .
Cee McSharpface
Gracias. Estaba pensando que el mensaje de correo debería hacer eso y lo necesito porque estoy creando mis mensajes en clases diferentes a donde realmente se envía el mensaje.
xr280xr
20

Si realmente desea agregar un .pdf, me pareció necesario establecer la posición del flujo de memoria en cero.

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);
Jason Dimmick
fuente
Dedique horas a enviar un pdf, ¡esto funcionó como un encanto!
Dijam
12

Si todo lo que está haciendo es adjuntar una cuerda, puede hacerlo en solo 2 líneas:

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

No pude hacer que el mío funcionara usando nuestro servidor de correo con StreamWriter.
Creo que tal vez porque con StreamWriter te falta mucha información de propiedad de archivos y tal vez a nuestro servidor no le gustó lo que faltaba.
Con Attachment.CreateAttachmentFromString () creó todo lo que necesitaba y funciona muy bien.

De lo contrario, sugiero tomar su archivo que está en la memoria y abrirlo usando MemoryStream (byte []), y omitir el StreamWriter todos juntos.

MikeTeeVee
fuente
2

Llegué a esta pregunta porque necesitaba adjuntar un archivo de Excel que genero a través del código y está disponible como MemoryStream. Podría adjuntarlo al mensaje de correo, pero se envió como un archivo de 64 bytes en lugar de un ~ 6 KB como estaba previsto. Entonces, la solución que funcionó para mí fue esta:

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);

Establecer el valor de attachment.ContentDisposition.SizePermítame enviar mensajes con el tamaño correcto de archivo adjunto.

ilForna
fuente
2

utilizar OTRO flujo de memoria ABIERTO:

ejemplo para lauch pdf y enviar pdf en 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);
    }
Ángel Ibáñez
fuente
stream.Position=0; es la línea que me ayudó. sin él, mi attcahment tenía solo 504 bytes instalados de algunos kBs
Abdul Hameed
-6

Creo que este código te ayudará:

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();
    }
  }
}
r12
fuente
5
-1 Esta respuesta carga el archivo adjunto desde el disco usando el constructor Attachment (string fileName). El OP indica específicamente que no quiere cargar desde el disco. Además, este es solo el código copiado y pegado del enlace en la respuesta de Red Swan.
Walter Stabosz
AttachFile stream no se elimina también
Sameh