iTextSharp - Envoi de pdf en mémoire dans une pièce jointe à un e-mail

100

J'ai posé quelques questions ici, mais j'ai toujours des problèmes. J'apprécierais si vous pouviez me dire ce que je fais mal dans mon code. J'exécute le code ci-dessus à partir d'une page ASP.Net et j'obtiens "Cannot Access a Closed Stream".

var doc = new Document();

MemoryStream memoryStream = new MemoryStream();

PdfWriter.GetInstance(doc, memoryStream);
doc.Open();
doc.Add(new Paragraph("First Paragraph"));
doc.Add(new Paragraph("Second Paragraph"));

doc.Close(); //if I remove this line the email attachment is sent but with 0 bytes 

MailMessage mm = new MailMessage("[email protected]", "[email protected]")
{
    Subject = "subject",
    IsBodyHtml = true,
    Body = "body"
};

mm.Attachments.Add(new Attachment(memoryStream, "test.pdf"));
SmtpClient smtp = new SmtpClient
{
    Host = "smtp.gmail.com",
    Port = 587,
    EnableSsl = true,
    Credentials = new NetworkCredential("[email protected]", "my_password")
};

smtp.Send(mm); //the "Cannot Access a Closed Stream" error is thrown here

Merci!!!

ÉDITER:

Juste pour aider quelqu'un qui cherche la réponse à cette question, le code pour envoyer un fichier pdf joint à un email sans avoir à créer physiquement le fichier est ci-dessous (merci à Ichiban et Brianng):

var doc = new Document();
MemoryStream memoryStream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream);

doc.Open();
doc.Add(new Paragraph("First Paragraph"));
doc.Add(new Paragraph("Second Paragraph"));

writer.CloseStream = false;
doc.Close();
memoryStream.Position = 0;

MailMessage mm = new MailMessage("[email protected]", "[email protected]")
{
    Subject = "subject",
    IsBodyHtml = true,
    Body = "body"
};

mm.Attachments.Add(new Attachment(memoryStream, "filename.pdf"));
SmtpClient smtp = new SmtpClient
{
    Host = "smtp.gmail.com",
    Port = 587,
    EnableSsl = true,
    Credentials = new NetworkCredential("[email protected]", "password")

};

smtp.Send(mm);
Gus Cavalcanti
la source
3
Merci d'avoir posé cette question, c'est exactement ce que je cherchais.
Hardwareguy
1
merci pour la ligne du position=0. m'a sauvé!
Yisroel M. Olewski
2
Exactement ce dont j'ai besoin fonctionne parfaitement merci beaucoup! J'étais coincé à la fermeture du document mais pas du flux: writer.CloseStream = false; éclairci pour moi.
Baxter
2
@Semil lorsque vous mettez une prime sur une vieille question avec une réponse acceptée, vous devez vraiment indiquer en quelque sorte ce que vous manquez dans la réponse.
mkl
writer.CloseStream = false; m'a sauvé aussi, il manquait cela dans une méthode qui utilise iTextSharp pour transformer HTML en PDF. Auparavant, la transmission du flux de mémoire à ma fonction de messagerie échouait en raison de la fermeture du flux. Merci.
Alec Menconi

Réponses:

81

As-tu essayé:

PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream);

// Build pdf code...

writer.CloseStream = false;
doc.Close();

// Build email

memoryStream.Position = 0;
mm.Attachments.Add(new Attachment(memoryStream, "test.pdf"));

Si ma mémoire est bonne, cela a résolu un problème similaire dans un projet précédent.

Voir http://forums.asp.net/t/1093198.aspx

brianng
la source
1
La méthode set_CloseStream n'est disponible que dans la version Java. C'est iTextSharp (.NET)
ichiban
Désolé, encore une fois, je n'ai pas utilisé iTextSharp (.NET) depuis un certain temps, même si la version que j'ai utilisée avait définitivement set_CloseStream.
brianng le
1
Changé en writer.CloseStream, et inclut un lien connexe.
brianng
1
Brianng, j'apprécie vraiment votre aide. Je réalise que vous et Ichiban avez en quelque sorte tenu ma main à travers. Merci!
Gus Cavalcanti
Si nous gardons l'écrivain en vie, quand devons-nous le faire writer.Flush()alors?
Blaise
18

J'ai essayé le code posté par brianng et cela a fonctionné. Changez simplement le haut du code comme suit:

var doc = new Document();
MemoryStream memoryStream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream); //capture the object
doc.Open();
doc.Add(new Paragraph("First Paragraph"));
doc.Add(new Paragraph("Second Paragraph"));
writer.CloseStream = false; //set the closestream property
doc.close(); //close the document without closing the underlying stream
memoryStream.Position = 0;

/* remainder of your code stays the same*/
ichiban
la source
3
Merci d'avoir pris le temps de vérifier!
brianng le
1
Salut Ichiban, Il compile et envoie en fait l'e-mail avec la pièce jointe, mais le document pdf joint est de 0 ko. Avez-vous réellement ouvert le pdf d'un e-mail envoyé?
Gus Cavalcanti
2
@Gustavo, le fichier s'ouvre correctement dans la visionneuse Acrobat. C'est environ 900 octets. Assurez-vous de conserver la ligne: memoryStream.Position = 0; juste après doc.Close (). J'ai oublié de mentionner cela. (voir mise à jour ci-dessus)
ichiban
1
OUI! Merci beaucoup les gars. Cela a finalement fonctionné. Puisque la réponse d'Ichiban était basée sur celle de brianng, je pense qu'il est juste de marquer la réponse de brianng comme correcte.
Gus Cavalcanti
3

Pouvez-vous vider le document ou le flux de mémoire, puis le fermer après l'avoir joint?

James Conigliaro
la source
Bonjour James. J'ai fait cela et le résultat n'a pas changé - j'obtiens toujours l'erreur "Impossible d'accéder à un flux fermé". :( Autres idées?
Gus Cavalcanti
3

Appelant probablement doc.Close () Supprime le flux sous-jacent. Essayez de supprimer doc.Close () et au lieu de cette ligne, définissez memoryStream.Position = 0;

Vous pouvez également utiliser un fichier temporaire:

var tempFilePath = Path.GetTempFileName();

try 
{           
    var doc = new Document();

    PdfWriter.GetInstance(doc, File.OpenWrite(tempFilePath));
    doc.Open();
    doc.Add(new Paragraph("First Paragraph"));
    doc.Add(new Paragraph("Second Paragraph"));

    doc.Close();

    MailMessage mm = new MailMessage("[email protected]", "[email protected]")
    {
        Subject = "subject",
        IsBodyHtml = true,
        Body = "body"
    };

    mm.Attachments.Add(new Attachment(tempFilePath, "test.pdf"));
    SmtpClient smtp = new SmtpClient
    {
        Host = "smtp.gmail.com",
        Port = 587,
        EnableSsl = true,
        Credentials = new NetworkCredential("[email protected]", "my_password")
    };

    smtp.Send(mm);
}
finally
{
    File.Delete(tempFilePath);
}
Huseyint
la source
huseyint, j'ai fait ce que vous avez suggéré et le fichier pdf est envoyé, mais il ne fait que 15 octets. Lorsque j'essaye de l'ouvrir, il est corrompu. Je sens que je suis presque là avec votre suggestion. D'autres idées? Merci!
Gus Cavalcanti
Ensuite, essayez memoryStream.Flush (); avant de définir la position
huseyint
Même chose. Le fichier est envoyé presque vide et corrompu. :(
Gus Cavalcanti
Avez-vous essayé "Créer un fichier temporaire"?
huseyint
J'y travaille en ce moment et je vous le ferai savoir sous peu. Merci!
Gus Cavalcanti
1

J'ai eu le même problème et j'ai utilisé ce post pour le résoudre.Dans le code écrit par brianng

PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream);

// Build pdf code...

writer.CloseStream = false;
doc.Close();

// Build email

memoryStream.Position = 0;
mm.Attachments.Add(new Attachment(memoryStream, "test.pdf"));

Je pense au lieu d'écrire

writer.CloseStream = false and memoryStream.Position = 0;

Créez simplement un nouveau flux

MemoryStream m = new MemoryStream(memoryStream);

puis appelez

mm.Attachments.Add(new Attachment(memoryStream, "test.pdf"));

Les deux fonctionnent mais je pense qu'il vaut mieux créer le nouveau flux

Zein Sleiman
la source
Pourquoi est-il préférable de créer un nouveau flux?
Andy
Ce n'est pas. C'est une perte de mémoire et de temps CPU car les octets doivent être copiés de l'un à l'autre.
Serguei Fedorov le
Je ne me souviens pas pourquoi j'ai dit que c'était mieux. Je pense que je pourrais dire que c'est plus clair. Désolé, je viens de voir ça. Cela fait longtemps :)
Zein Sleiman