Anexe um arquivo de MemoryStream a uma MailMessage em C #

113

Estou escrevendo um programa para anexar um arquivo a um e-mail. Atualmente estou salvando o arquivo usando FileStreamno disco, e então uso

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

Não quero armazenar o arquivo no disco, quero armazenar o arquivo na memória e do fluxo de memória passar isso para Attachment.

Zain Ali
fonte

Respostas:

104

Aqui está o código de exemplo.

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

Você pode especificar outros tipos de arquivo por System.Net.Mime.MimeTypeNames como System.Net.Mime.MediaTypeNames.Application.Pdf

Com base no tipo Mime, você precisa especificar a extensão correta em FileName, por exemplo"myFile.pdf"

Waqas Raja
fonte
Estou usando PDF como passar este tipo para System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType (System.Net.Mime.MediaTypeNames.Text.Plain);
Zain Ali
3
Você precisa usarSystem.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja
5
writer.Disopose()era muito cedo para minha solução, mas todos os outros são um ótimo exemplo.
Kazimierz Jawor
7
Tenho certeza de que deve haver um ms.Position = 0;antes de criar o anexo.
Kenny Evitt
94

Uma entrada um pouco tardia - mas espero que ainda seja útil para alguém por aí: -

Aqui está um snippet simplificado para enviar uma string na memória como um anexo de e-mail (um arquivo CSV neste caso específico).

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

O StreamWriter e o fluxo subjacente não devem ser descartados até que a mensagem tenha sido enviada (para evitar ObjectDisposedException: Cannot access a closed Stream).

Tarn tranquilo
fonte
30
Para qualquer novato, a chave para mim era definir ostream.position = 0;
mtbennett
3
+1 para o uso apropriado de using () 's - algo que sempre parece estar faltando nos exemplos e snippets online (incluindo a resposta aceita para esta pergunta).
Jay Querido
2
Graças a @mtbennet, esse também foi o meu problema - definir stream.Position=0.
Ícaro
O que a configuração do tipo MIME realmente faz aqui? Algo para o aplicativo cliente de e-mail?
xr280xr
@ xr280xr - correto. Este parâmetro não é realmente necessário, mas incluí-lo deve ajudar o cliente de e-mail do destinatário a lidar com o anexo de maneira sensata. docs.microsoft.com/en-us/dotnet/api/…
tranquilo tarn
28

Como não consegui encontrar a confirmação disso em lugar nenhum, testei se o descarte do MailMessage e / ou do objeto Attachment eliminaria o fluxo carregado neles como eu esperava que aconteceria.

Parece com o seguinte teste que, quando MailMessage é descartado, todos os fluxos usados ​​para criar anexos também serão descartados. Portanto, contanto que você descarte sua MailMessage, os fluxos que foram usados ​​para criá-la não precisam de tratamento além disso.

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
fonte
Droga, isso é inteligente. Uma linha de código e você pode adicionar um arquivo de imagem a um e-mail como um anexo. Ótima dica!
Mike Gledhill
Eu gostaria que isso fosse realmente documentado. Seu teste / verificação deste comportamento é útil, mas sem estar na documentação oficial é difícil confiar que esse sempre será o caso. De qualquer forma, obrigado pelo teste.
Lucas
Good Effort and Research @thymine
vibs2006
1
é meio que documentado, se a fonte de referência contar. mailmessage.dispose chama attachments.dispose, que por sua vez, as chamadas descartam cada anexo que, em mimepart, fecha o fluxo .
Cee McSharpface,
Obrigado. Eu estava pensando que a mensagem de correio deveria fazer isso e precisa disso porque estou criando minhas mensagens em classes diferentes daquelas para onde a mensagem é realmente enviada.
xr280xr
20

Se você realmente deseja adicionar um .pdf, achei necessário definir a posição do fluxo de memória para 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);
Jason Dimmick
fonte
Gaste horas enviando um pdf funcionou como um encanto!
dijam 01 de
12

Se tudo o que você está fazendo é anexar uma string, você pode fazer isso em apenas 2 linhas:

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

Não consegui fazer o meu funcionar usando nosso servidor de e-mail com StreamWriter.
Acho que talvez porque com o StreamWriter você esteja perdendo muitas informações de propriedade do arquivo e talvez nosso servidor não tenha gostado do que estava faltando.
Com Attachment.CreateAttachmentFromString (), ele criou tudo que eu precisava e funciona muito bem!

Caso contrário, sugiro pegar o arquivo que está na memória e abri-lo usando MemoryStream (byte []) e pular o StreamWriter todos juntos.

MikeTeeVee
fonte
2

I pousou sobre esta questão, porque eu precisava para anexar um arquivo do Excel eu gerar através de código e está disponível como MemoryStream. Eu poderia anexá-lo à mensagem de e-mail, mas ele foi enviado como um arquivo de 64 bytes em vez de aproximadamente 6 KB, como deveria. Então, a solução que funcionou para mim foi 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);

Definir o valor de attachment.ContentDisposition.Sizedeixe-me enviar mensagens com o tamanho correto de anexo.

ilForna
fonte
2

use OUTRO fluxo de memória ABERTO:

exemplo para lançar pdf e enviar pdf em 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
fonte
stream.Position=0; é a linha que me ajudou. sem ele, meu anexo era de apenas 504 bytes instaurado de alguns kBs
Abdul Hameed
-6

Acho que este código vai te ajudar:

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
fonte
5
-1 Esta resposta carrega o anexo do disco usando o construtor Attachment (string fileName). O OP declara especificamente que não deseja carregar do disco. Além disso, este é apenas o código copiado e colado do link na resposta de Red Swan.
Walter Stabosz
O stream attachFile também não foi descartado
Sameh