Joindre un fichier de MemoryStream à un MailMessage en C #

113

J'écris un programme pour joindre un fichier à un e-mail. Actuellement, j'enregistre le fichier en utilisant FileStreamsur le disque, puis j'utilise

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

Je ne veux pas stocker de fichier sur le disque, je veux stocker le fichier en mémoire et à partir du flux de mémoire, passez-le à Attachment.

Zain Ali
la source

Réponses:

104

Voici l exemple de code.

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

Modifier 1

Vous pouvez spécifier d'autres types de fichiers par System.Net.Mime.MimeTypeNames comme System.Net.Mime.MediaTypeNames.Application.Pdf

En fonction du type Mime, vous devez spécifier l'extension correcte dans FileName par exemple"myFile.pdf"

Waqas Raja
la source
J'utilise PDF Comment passer ce type à System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType (System.Net.Mime.MediaTypeNames.Text.Plain);
Zain Ali
3
Vous devez utiliserSystem.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja
5
writer.Disopose()était trop tôt pour ma solution mais tous les autres sont un excellent exemple.
Kazimierz Jawor
7
Je suis presque sûr qu'il devrait y avoir un ms.Position = 0;avant de créer la pièce jointe.
Kenny Evitt
94

Une entrée un peu tardive - mais j'espère toujours utile à quelqu'un là-bas: -

Voici un extrait de code simplifié pour envoyer une chaîne en mémoire sous forme de pièce jointe à un e-mail (un fichier CSV dans ce cas particulier).

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

Le StreamWriter et le flux sous-jacent ne doivent pas être supprimés tant que le message n'a pas été envoyé (à éviter ObjectDisposedException: Cannot access a closed Stream).

tarn tranquille
la source
30
Pour tous les nouveaux arrivants, la clé pour moi était de régler lestream.position = 0;
mtbennett
3
+1 pour une utilisation appropriée de using () - quelque chose qui semble toujours manquer dans les exemples et les extraits en ligne (y compris la réponse acceptée à cette question).
Jay Querido
2
Merci à @mtbennet, c'était aussi mon problème stream.Position=0.
Icarus
Que fait la définition du type MIME ici? Quelque chose pour l'application client e-mail?
xr280xr
@ xr280xr - Correct. Ce paramètre n'est pas réellement obligatoire, mais son inclusion devrait aider le client de messagerie du destinataire à gérer la pièce jointe de manière judicieuse. docs.microsoft.com/en-us/dotnet/api/…
tranquil tarn
28

Étant donné que je n'ai trouvé aucune confirmation de cela, j'ai testé si l'élimination du MailMessage et / ou de l'objet Attachment éliminerait le flux chargé dans ces derniers comme je m'y attendais.

Il apparaît avec le test suivant que lorsque le MailMessage est supprimé, tous les flux utilisés pour créer des pièces jointes seront également supprimés. Donc, tant que vous supprimez votre MailMessage, les flux qui ont servi à sa création n'ont pas besoin d'être traités au-delà de cela.

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);
Thymine
la source
Merde, c'est intelligent. Une ligne de code et vous pouvez ajouter un fichier image à un e-mail en tant que pièce jointe. Bon conseil!
Mike Gledhill
Je souhaite que cela soit réellement documenté. Votre test / vérification de ce comportement est utile, mais sans être dans la documentation officielle, il est difficile de croire que ce sera toujours le cas. Quoi qu'il en soit, merci pour les tests.
Lucas
Good Effort and Research @thymine
vibs2006
1
c'est en quelque sorte documenté, si la source de référence compte. mailmessage.dispose appelle attachments.dispose qui à son tour, les appels disposent sur chaque pièce jointe qui, dans mimepart, ferme le flux .
Cee McSharpface
Je vous remercie. Je pensais que le message électronique devrait faire cela et en avoir besoin parce que je crée mes messages dans des classes différentes de celles où le message est réellement envoyé.
xr280xr
20

Si vous souhaitez réellement ajouter un .pdf, j'ai trouvé nécessaire de définir la position du flux de mémoire sur zéro.

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
la source
Passez des heures à envoyer un pdf, cela a fonctionné comme un charme!
dijam
12

Si vous ne faites que joindre une chaîne, vous pouvez le faire en seulement 2 lignes:

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

Je n'ai pas pu faire fonctionner le mien en utilisant notre serveur de messagerie avec StreamWriter.
Je pense que peut-être parce qu'avec StreamWriter il vous manque beaucoup d'informations sur les propriétés de fichiers et peut-être que notre serveur n'a pas aimé ce qui manquait.
Avec Attachment.CreateAttachmentFromString (), il a créé tout ce dont j'avais besoin et fonctionne très bien!

Sinon, je suggère de prendre votre fichier qui est en mémoire et de l'ouvrir à l'aide de MemoryStream (octet []), et de sauter le StreamWriter tous ensemble.

MikeTeeVee
la source
2

J'ai atterri sur cette question car j'avais besoin de joindre un fichier Excel que je génère via du code et qui est disponible en tant que MemoryStream. Je pourrais le joindre au message électronique, mais il a été envoyé sous forme de fichier de 64 octets au lieu de ~ 6 Ko comme prévu. Donc, la solution qui a fonctionné pour moi était la suivante:

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

Définition de la valeur de attachment.ContentDisposition.Sizelaissez-moi envoyer des messages avec la taille correcte de la pièce jointe.

ilForna
la source
2

utiliser AUTRE flux de mémoire OPEN:

exemple pour lancer un pdf et envoyer un pdf dans 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
la source
stream.Position=0; est la ligne qui m'a aidé. sans cela, mon attachement ne faisait que 504 octets instaed de quelques kBs
Abdul Hameed
-6

Je pense que ce code vous aidera:

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
la source
5
-1 Cette réponse charge la pièce jointe à partir du disque à l'aide du constructeur Attachment (string fileName). L'OP déclare spécifiquement qu'il ne veut pas charger à partir du disque. De plus, ce n'est que le code copié à partir du lien dans la réponse de Red Swan.
Walter Stabosz
Le flux attachFile n'est pas supprimé également
Sameh