iTextSharp - Enviando PDF na memória em um anexo de e-mail

100

Eu fiz algumas perguntas aqui, mas ainda estou tendo problemas. Eu apreciaria se você pudesse me dizer o que estou fazendo de errado no meu código. Eu executo o código acima de uma página ASP.Net e recebo "Não é possível acessar um fluxo fechado".

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

Obrigado!!!

EDITAR:

Só para ajudar quem está procurando a resposta a esta pergunta, segue abaixo o código para enviar um arquivo pdf anexado a um e-mail sem ter que criar fisicamente o arquivo (obrigado a Ichiban e 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
fonte
3
Obrigado por fazer esta pergunta, é exatamente o que eu estava procurando.
Hardwareguy
1
obrigado pela linha do position=0. me salvou!
Yisroel M. Olewski de
2
Exatamente o que eu preciso funciona perfeitamente, muito obrigado! Não consegui fechar o documento, mas não o stream: writer.CloseStream = false; esclareceu para mim.
Baxter
2
@Semil ao colocar uma recompensa em uma pergunta antiga com uma resposta aceita, você realmente deve indicar de alguma forma o que você perdeu na resposta.
mkl
escritor.CloseStream = false; me salvou também, estava faltando isso em um método que usa o iTextSharp para transformar HTML em PDF. Antes, a passagem do fluxo de memória para minha função de e-mail falhava devido ao fechamento do fluxo. Obrigado.
Alec Menconi

Respostas:

81

Você tentou:

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

Se minha memória não me falha, isso resolveu um problema semelhante em um projeto anterior.

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

brianng
fonte
1
O método set_CloseStream está disponível apenas na versão Java. Este é o iTextSharp (.NET)
ichiban
Desculpe, novamente eu não uso o iTextSharp (.NET) há algum tempo, embora a versão que usei definitivamente tinha set_CloseStream.
brianng
1
Alterado para writer.CloseStream e inclui link relacionado.
brianng
1
Brianng, eu realmente aprecio sua ajuda. Eu percebo que você e Ichiban meio que seguraram minha mão através dele. Obrigado!
Gus Cavalcanti
Se mantivermos o escritor vivo, quando devemos fazer writer.Flush()isso?
Blaise
18

Tentei o código postado por brianng e funcionou. Basta alterar o topo do código para este:

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
fonte
3
Obrigado por reservar um tempo para verificar!
brianng
1
Olá Ichiban, Compila e envia o e-mail com o anexo, mas o documento PDF anexado tem 0kb. Você realmente abriu o pdf que um e-mail enviou?
Gus Cavalcanti
2
@Gustavo, o arquivo abre corretamente no visualizador Acrobat. Tem cerca de 900 bytes. Certifique-se de manter a linha: memoryStream.Position = 0; logo após doc.Close (). Eu esqueci de mencionar isso. (ver atualização acima)
ichiban
1
SIM! Muito obrigado pessoal. Finalmente funcionou. Já que a resposta de Ichiban foi baseada na de brianng, acho que é justo marcar a resposta de brianng como correta.
Gus Cavalcanti
3

Você pode liberar o documento ou fluxo de memória e fechá-lo depois de anexá-lo?

James Conigliaro
fonte
Olá James. Fiz isso e o resultado não mudou - continuo recebendo o erro "Não é possível acessar um stream fechado". :( Outras ideias?
Gus Cavalcanti
3

Provavelmente chamando doc.Close () descarta o fluxo subjacente. Tente remover doc.Close () e em vez dessa linha defina memoryStream.Position = 0;

Como alternativa, você pode usar um arquivo temporário:

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
fonte
huseyint, fiz o que você sugeriu e o arquivo pdf é enviado, mas ele tem apenas 15 bytes. Quando tento abri-lo, ele está corrompido. Sinto que estou quase lá com sua sugestão. Alguma outra ideia? Obrigado!
Gus Cavalcanti
Em seguida, tente memoryStream.Flush (); antes de definir a posição
huseyint
Mesma coisa. O arquivo é enviado quase vazio e corrompido. :(
Gus Cavalcanti
Você tentou "Criar um arquivo temporário"?
huseyint
Estou trabalhando nisso agora e avisarei você em breve. Obrigado!
Gus Cavalcanti
1

Tive o mesmo problema e usei este post para resolvê-lo. No código escrito por 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"));

Eu penso em vez de escrever

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

Basta criar um novo fluxo

MemoryStream m = new MemoryStream(memoryStream);

e então ligar

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

Ambos funcionam, mas acho que é melhor criar o novo fluxo

Zein Sleiman
fonte
Por que é melhor criar um novo fluxo?
Andy
Não é. É uma perda de memória e tempo de CPU porque os bytes têm que ser copiados de um para o outro.
Serguei Fedorov
Não me lembro porque disse que é melhor. Acho que quis dizer que é mais claro. Desculpe, só vi isso. Faz muito tempo :)
Zein Sleiman