Dołącz plik z MemoryStream do MailMessage w C #

113

Piszę program do załączania pliku do wiadomości e-mail. Obecnie zapisuję plik za pomocą FileStreamna dysku, a następnie używam

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

Nie chcę przechowywać pliku na dysku, chcę przechowywać plik w pamięci i ze strumienia pamięci przekazać to do Attachment.

Zain Ali
źródło

Odpowiedzi:

104

Oto przykładowy kod.

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

Edytuj 1

Możesz określić inne typy plików za pomocą System.Net.Mime.MimeTypeNames, takich jak System.Net.Mime.MediaTypeNames.Application.Pdf

Na podstawie typu Mime musisz na przykład określić poprawne rozszerzenie w FileName"myFile.pdf"

Waqas Raja
źródło
Używam PDF Jak przekazać ten typ do System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType (System.Net.Mime.MediaTypeNames.Text.Plain);
Zain Ali
3
Musisz skorzystaćSystem.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja
5
writer.Disopose()było za wcześnie na moje rozwiązanie, ale wszystkie inne są świetnym przykładem.
Kazimierz Jawor
7
Jestem prawie pewien, że ms.Position = 0;przed utworzeniem załącznika powinien być .
Kenny Evitt
94

Trochę późny wpis - ale mam nadzieję, że nadal komuś tam się przyda: -

Oto uproszczony fragment do wysyłania ciągu w pamięci jako załącznika do wiadomości e-mail (w tym konkretnym przypadku plik CSV).

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

StreamWriter i źródłowy strumień nie powinny być usuwane, dopóki nie zostanie wysłany komunikat (aby uniknąć ObjectDisposedException: Cannot access a closed Stream).

spokojny Tarn
źródło
30
Dla każdego nowicjusza kluczem było ustawieniestream.position = 0;
mtbennett
3
+1 za właściwe użycie using () - czegoś, czego zawsze brakuje w przykładach i fragmentach online (w tym w akceptowanej odpowiedzi na to pytanie).
Jay Querido
2
Dzięki @mtbennet to też był mój problem - ustawienie stream.Position=0.
Icarus
Co właściwie robi tutaj ustawienie typu MIME? Coś dla aplikacji klienta poczty e-mail?
xr280xr
@ xr280xr - dobrze. Ten parametr nie jest w rzeczywistości wymagany, ale uwzględnienie go powinno pomóc klientowi poczty e-mail odbiorcy rozsądnie obsłużyć załącznik. docs.microsoft.com/en-us/dotnet/api/ ...
tranquil tarn
28

Ponieważ nigdzie nie mogłem znaleźć potwierdzenia tego, przetestowałem, czy pozbycie się MailMessage i / lub obiektu Attachment spowoduje usunięcie załadowanego do nich strumienia, tak jak się spodziewałem.

W przypadku następującego testu wygląda na to, że po usunięciu wiadomości MailMessage wszystkie strumienie używane do tworzenia załączników również zostaną usunięte. Tak długo, jak usuniesz swoją MailMessage, strumienie, które zostały użyte do jej utworzenia, nie będą wymagały obsługi poza tym.

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);
Tymina
źródło
Cholera, to sprytne. Jedna linia kodu i możesz dodać plik obrazu do wiadomości e-mail jako załącznik. Świetna wskazówka!
Mike Gledhill
Chciałbym, żeby to było rzeczywiście udokumentowane. Twój test / weryfikacja tego zachowania jest przydatna, ale bez oficjalnej dokumentacji trudno ufać, że tak będzie zawsze. W każdym razie dziękuję za test.
Lucas
Good Effort and Research @thymine
vibs2006
1
jest to w pewnym sensie udokumentowane, jeśli liczy się źródło odniesienia. mailmessage.dispose wywołuje attachments.dispose, które z kolei wywołują każdy załącznik, który w mimepart zamyka strumień .
Cee McSharpface,
Dziękuję Ci. Pomyślałem, że wiadomość e-mail powinna to robić i potrzebuję tego, ponieważ tworzę wiadomości w innych klasach niż te, w których wiadomość jest faktycznie wysyłana.
xr280xr
20

Jeśli faktycznie chcesz dodać plik .pdf, uznałem za konieczne ustawienie pozycji strumienia pamięci na 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
źródło
Poświęć wiele godzin na wysłanie pliku PDF, który działał jak urok!
święto
12

Jeśli wszystko, co robisz, to dołączanie łańcucha, możesz to zrobić w zaledwie 2 wierszach:

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

Nie mogłem zmusić mojego do pracy przy użyciu naszego serwera pocztowego ze StreamWriter.
Myślę, że może dlatego, że w przypadku StreamWriter brakuje wielu informacji o właściwościach plików, a może nasz serwer nie spodobał się tego, czego brakowało.
Dzięki Attachment.CreateAttachmentFromString () stworzył wszystko, czego potrzebowałem i działa świetnie!

W przeciwnym razie sugerowałbym wzięcie pliku, który jest w pamięci i otwarcie go za pomocą MemoryStream (bajt []) i pominięcie StreamWriter razem.

MikeTeeVee
źródło
2

Wylądowałem na tym pytaniu, ponieważ musiałem załączyć plik Excel, który wygenerowałem za pomocą kodu i jest dostępny jako MemoryStream. Mogłem dołączyć go do wiadomości e-mail, ale został wysłany jako plik 64-bajtowy zamiast ~ 6KB, jak było to zamierzone. Tak więc rozwiązanie, które działało dla mnie, było następujące:

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

Ustawienie wartości opcji attachment.ContentDisposition.Sizepozwól mi wysyłać wiadomości z odpowiednim rozmiarem załącznika.

ilForna
źródło
2

użyj INNEGO OTWARTEGO strumienia pamięci:

przykład dla lauch pdf i wyślij pdf w kontrolerze MVC4 C #

        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
źródło
stream.Position=0; to linia, która mi pomogła. bez tego moja uwaga wynosiła tylko 504 bajty zamiast niektórych KB
Abdul Hameed
-6

Myślę, że ten kod ci pomoże:

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
źródło
5
-1 Ta odpowiedź ładuje załącznik z dysku przy użyciu konstruktora Attachment (string fileName). OP wyraźnie stwierdza, że ​​nie chce ładować z dysku. To tylko kod skopiowany z linku w odpowiedzi Red Swan.
Walter Stabosz
Strumień attachFile również nie jest usuwany
Sameh