Hängen Sie eine Datei aus MemoryStream an eine MailMessage in C # an

113

Ich schreibe ein Programm, um eine Datei an eine E-Mail anzuhängen. Derzeit speichere ich Datei mit FileStreamauf Festplatte, und dann verwende ich

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

Ich möchte keine Datei auf der Festplatte speichern, ich möchte eine Datei im Speicher speichern und diese vom Speicherstrom an weiterleiten Attachment.

Zain Ali
quelle

Antworten:

104

Hier ist der Beispielcode.

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

Bearbeiten 1

Sie können andere Dateitypen über System.Net.Mime.MimeTypeNames wie angeben System.Net.Mime.MediaTypeNames.Application.Pdf

Basierend auf dem MIME-Typ müssen Sie beispielsweise in FileName die richtige Erweiterung angeben"myFile.pdf"

Waqas Raja
quelle
Ich verwende PDF So übergeben Sie diesen Typ an System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType (System.Net.Mime.MediaTypeNames.Text.Plain);
Zain Ali
3
Sie müssen verwendenSystem.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja
5
writer.Disopose()war zu früh für meine Lösung, aber alles andere ist ein gutes Beispiel.
Kazimierz Jawor
7
Ich bin mir ziemlich sicher, dass es ms.Position = 0;vor dem Erstellen des Anhangs einen geben sollte.
Kenny Evitt
94

Ein bisschen verspäteter Eintrag - aber hoffentlich immer noch nützlich für jemanden da draußen: -

Hier ist ein vereinfachtes Snippet zum Senden einer speicherinternen Zeichenfolge als E-Mail-Anhang (in diesem speziellen Fall eine CSV-Datei).

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

Der StreamWriter und der zugrunde liegende Stream sollten erst nach dem Senden der Nachricht entsorgt werden (um dies zu vermeiden ObjectDisposedException: Cannot access a closed Stream).

ruhiger Tarn
quelle
30
Für alle Neulinge war der Schlüssel für mich, dasstream.position = 0;
mtbennett
3
+1 für die angemessene Verwendung von using () - etwas, das in Beispielen und Snippets online immer zu fehlen scheint (einschließlich der akzeptierten Antwort auf diese Frage).
Jay Querido
2
Dank @mtbennet war das auch meine Problemstellung stream.Position=0.
Ikarus
Was macht das Einstellen des MIME-Typs hier eigentlich? Etwas für die E-Mail-Client-App?
xr280xr
@ xr280xr - Richtig. Dieser Parameter ist eigentlich nicht erforderlich, sollte jedoch dem E-Mail-Client des Empfängers helfen, den Anhang sinnvoll zu handhaben. docs.microsoft.com/en-us/dotnet/api/…
ruhiger Tarn
28

Da ich nirgendwo eine Bestätigung dafür finden konnte, habe ich getestet, ob das Entsorgen der MailMessage und / oder des Attachment-Objekts den in sie geladenen Stream wie erwartet entsorgen würde.

Beim folgenden Test wird angezeigt, dass beim Entsorgen der MailMessage alle zum Erstellen von Anhängen verwendeten Streams ebenfalls entsorgt werden. Solange Sie Ihre MailMessage entsorgen, müssen die Streams, mit denen sie erstellt wurden, nicht darüber hinaus verarbeitet werden.

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);
Thymin
quelle
Verdammt, das ist klug. Eine Codezeile, und Sie können einer E-Mail eine Bilddatei als Anhang hinzufügen. Toller Tipp!
Mike Gledhill
Ich wünschte, dies wäre tatsächlich dokumentiert. Ihr Test / Ihre Überprüfung dieses Verhaltens ist nützlich, aber ohne in der offiziellen Dokumentation zu stehen, ist es schwierig zu vertrauen, dass dies immer der Fall sein wird. Trotzdem danke fürs Testen.
Lucas
Gute Anstrengung und Forschung @thymine
vibs2006
1
Es ist irgendwie dokumentiert, wenn die Referenzquelle zählt. mailmessage.dispose ruft attachments.dispose auf, die wiederum Aufrufe für jeden Anhang bereitstellen, der in mimepart den Stream schließt .
Cee McSharpface
Danke dir. Ich dachte, dass die E-Mail-Nachricht das tun sollte und muss, weil ich meine Nachrichten in anderen Klassen erstelle, als dort, wo die Nachricht tatsächlich gesendet wird.
xr280xr
20

Wenn Sie tatsächlich eine PDF-Datei hinzufügen möchten, musste die Position des Speicherstroms auf Null gesetzt werden.

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
quelle
Verbringen Sie Stunden damit, ein PDF zu senden. Das hat wie ein Zauber gewirkt!
Dijam
12

Wenn Sie nur eine Zeichenfolge anhängen, können Sie dies in nur zwei Zeilen tun:

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

Ich konnte meine mit StreamWriter nicht über unseren Mailserver zum Laufen bringen.
Ich denke, vielleicht, weil Sie mit StreamWriter viele Informationen zu Dateieigenschaften vermissen und unser Server vielleicht nicht mochte, was fehlte.
Mit Attachment.CreateAttachmentFromString () wurde alles erstellt, was ich brauchte, und es funktioniert großartig!

Andernfalls würde ich vorschlagen, Ihre im Speicher befindliche Datei mit MemoryStream (Byte []) zu öffnen und den StreamWriter insgesamt zu überspringen.

MikeTeeVee
quelle
2

Ich bin auf diese Frage gestoßen, weil ich eine Excel-Datei anhängen musste, die ich über Code generiere und die als verfügbar ist MemoryStream. Ich konnte es an die E-Mail-Nachricht anhängen, aber es wurde als 64-Byte-Datei anstelle von ~ 6 KB gesendet, wie es gemeint war. Die Lösung, die für mich funktioniert hat, war folgende:

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

Festlegen des Werts von attachment.ContentDisposition.SizeLassen Sie mich Nachrichten mit der richtigen Größe des Anhangs senden.

ilForna
quelle
2

Verwenden Sie ANDEREN OFFENEN Memorystream:

Beispiel für lauch pdf und pdf in MVC4 C # Controller senden

        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
quelle
stream.Position=0; ist die Linie, die mir geholfen hat. ohne sie war meine attcahment nur 504 Bytes von einigen kBs instaed
Abdul Hameed
-6

Ich denke, dieser Code wird Ihnen helfen:

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
quelle
5
-1 Diese Antwort lädt den Anhang mithilfe des Konstruktors Anhang (Zeichenfolge Dateiname) von der Festplatte. Das OP gibt ausdrücklich an, dass er nicht von der Festplatte laden möchte. Dies ist auch nur der Code, der aus dem Link in Red Swans Antwort kopiert wurde.
Walter Stabosz
attachFile Stream wird auch nicht entsorgt
Sameh