iTextSharp - wysyłanie pliku PDF w pamięci w załączniku do wiadomości e-mail

100

Zadałem tutaj kilka pytań, ale nadal mam problemy. Byłbym wdzięczny, gdybyś mógł mi powiedzieć, co robię źle w swoim kodzie. Uruchamiam powyższy kod ze strony ASP.Net i otrzymuję komunikat „Nie można uzyskać dostępu do zamkniętego strumienia”.

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

Dzięki!!!

EDYTOWAĆ:

Aby pomóc komuś, kto szuka odpowiedzi na to pytanie, kod do wysłania pliku pdf załączonego do e-maila bez konieczności fizycznego tworzenia pliku znajduje się poniżej (dzięki Ichiban i 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
źródło
3
Dzięki za zadanie tego pytania, właśnie tego szukałem.
Hardwareguy
1
dzięki za linię position=0. uratował mnie!
Yisroel M. Olewski
2
Dokładnie to, czego potrzebuję, działa idealnie, bardzo dziękuję! Utknąłem podczas zamykania dokumentu, ale nie strumienia: writer.CloseStream = false; wyjaśniło to dla mnie.
Baxter,
2
@Semil, stawiając nagrodę za stare pytanie z zaakceptowaną odpowiedzią, naprawdę powinieneś w jakiś sposób wskazać, czego w odpowiedzi brakuje.
mkl
writer.CloseStream = false; uratował mnie również, brakowało tego w metodzie, która używa iTextSharp do zamiany HTML na PDF. Wcześniej przekazanie strumienia pamięci do mojej funkcji poczty nie powiodło się z powodu zamknięcia strumienia. Dzięki.
Alec Menconi

Odpowiedzi:

81

Czy próbowałeś:

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śli moja pamięć dobrze mi służy, rozwiązało to podobny problem w poprzednim projekcie.

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

brianng
źródło
1
Metoda set_CloseStream jest dostępna tylko w wersji Java. To jest iTextSharp (.NET)
ichiban
Przepraszam, znowu nie korzystałem z iTextSharp (.NET) od jakiegoś czasu, chociaż wersja, której użyłem, na pewno miała set_CloseStream.
brianng
1
Zmieniono na writer.CloseStream i dołączono powiązany link.
brianng
1
Brianng, naprawdę doceniam twoją pomoc. Zdaję sobie sprawę, że ty i Ichiban trzymaliście przez to moją rękę. Dzięki!
Gus Cavalcanti
Jeśli utrzymamy pisarza przy życiu, kiedy to przypuszczamy writer.Flush()?
Blaise,
18

Wypróbowałem kod przesłany przez brianng i zadziałał. Po prostu zmień górę kodu na następującą:

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
źródło
3
Dziękujemy za poświęcenie czasu na weryfikację!
brianng
1
Cześć Ichiban, kompiluje się i faktycznie wysyła wiadomość e-mail z załącznikiem, ale załączony dokument PDF ma 0kb. Czy faktycznie otworzyłeś plik PDF wysłany przez e-mail?
Gus Cavalcanti
2
@Gustavo, plik otwiera się poprawnie w przeglądarce Acrobat. To około 900 bajtów. Upewnij się, że trzymasz wiersz: memoryStream.Position = 0; zaraz po doc.Close (). Zapomniałem o tym wspomnieć. (patrz aktualizacja powyżej)
ichiban
1
TAK! Bardzo wam dziękuję. W końcu zadziałało. Ponieważ odpowiedź Ichibana została oparta na odpowiedzi briannga, myślę, że uczciwie jest oznaczyć odpowiedź briannga jako poprawną.
Gus Cavalcanti
3

Czy możesz opróżnić dokument lub strumień pamięci, a następnie zamknąć go po dołączeniu?

James Conigliaro
źródło
Cześć James. Zrobiłem to i wynik się nie zmienił - nadal pojawia się błąd „Nie można uzyskać dostępu do zamkniętego strumienia”. :( Inne pomysły?
Gus Cavalcanti
3

Prawdopodobnie wywołanie doc.Close () Usuwa źródłowy strumień. Spróbuj usunąć doc.Close () i zamiast tej linii ustaw memoryStream.Position = 0;

Alternatywnie możesz użyć pliku tymczasowego:

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
źródło
huseyint, zrobiłem to, co zasugerowałeś i plik pdf jest wysyłany, ale ma tylko 15 bajtów. Kiedy próbuję go otworzyć, jest uszkodzony. Czuję, że jestem prawie na miejscu z twoją sugestią. Jakieś inne pomysły? Dzięki!
Gus Cavalcanti
Następnie spróbuj memoryStream.Flush (); przed ustaleniem pozycji
huseyint
Ta sama rzecz. Plik jest wysyłany prawie pusty i uszkodzony. :(
Gus Cavalcanti
Czy wypróbowałeś „Tworzenie pliku tymczasowego”?
huseyint
Pracuję nad tym teraz i wkrótce dam znać. Dzięki!
Gus Cavalcanti
1

Miałem ten sam problem i wykorzystałem ten post do rozwiązania go w kodzie napisanym przez briannga

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

Myślę, zamiast pisać

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

Po prostu utwórz nowy strumień

MemoryStream m = new MemoryStream(memoryStream);

a następnie zadzwoń

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

Oba działają, ale myślę, że lepiej jest utworzyć nowy strumień

Zein Sleiman
źródło
Dlaczego lepiej jest utworzyć nowy strumień?
Andy
To nie jest. To strata pamięci i czasu procesora, ponieważ bajty muszą być kopiowane z jednego do drugiego.
Serguei Fedorov
Nie pamiętam, dlaczego powiedziałem, że jest lepiej. Myślę, że mógłbym powiedzieć, że jest jaśniejszy. Przepraszam, właśnie to widziałem. Minęło dużo czasu :)
Zein Sleiman