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.

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"


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("me@example.com", "you@example.com", "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).


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

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

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.


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.


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

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.