iTextSharp: envío de PDF en memoria en un archivo adjunto de correo electrónico

100

He hecho un par de preguntas aquí, pero todavía tengo problemas. Le agradecería que me dijera qué estoy haciendo mal en mi código. Ejecuto el código anterior desde una página ASP.Net y obtengo "No se puede acceder a una secuencia cerrada".

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

¡¡¡Gracias!!!

EDITAR:

Solo para ayudar a alguien que busque la respuesta a esta pregunta, el código para enviar un archivo pdf adjunto a un correo electrónico sin tener que crear físicamente el archivo se encuentra a continuación (gracias a Ichiban y 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
fuente
3
Gracias por hacer esta pregunta, es exactamente lo que estaba buscando.
Hardwareguy
1
gracias por la línea del position=0. ¡me salvó!
Yisroel M. Olewski
2
Exactamente lo que necesito funciona perfectamente, ¡muchas gracias! Me quedé atascado al cerrar el documento pero no la secuencia: writer.CloseStream = false; me lo aclaró.
Baxter
2
@Semil cuando ofrezca una recompensa por una pregunta anterior con una respuesta aceptada, realmente debe indicar de alguna manera lo que se pierde en la respuesta.
mkl
escritor.CloseStream = falso; también me salvó, faltaba eso en un método que usa iTextSharp para convertir HTML a PDF. Antes, pasar el flujo de memoria a mi función de correo falló debido a que el flujo se cerró. Gracias.
Alec Menconi

Respuestas:

81

Has probado:

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 mi memoria no me falla, esto resolvió un problema similar en un proyecto anterior.

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

Brianng
fuente
1
El método set_CloseStream solo está disponible en la versión de Java. Esto es iTextSharp (.NET)
ichiban
Lo siento, nuevamente no he usado iTextSharp (.NET) por un tiempo, aunque la versión que usé definitivamente tenía set_CloseStream.
brianng
1
Se cambió a writer.CloseStream e incluyó un enlace relacionado.
brianng
1
Brianng, realmente agradezco tu ayuda. Me doy cuenta de que Ichiban y tú me cogieron la mano. ¡Gracias!
Gus Cavalcanti
Si mantenemos vivo al escritor, ¿cuándo se supone que lo haremos writer.Flush()?
Blaise
18

Probé el código publicado por brianng y funcionó. Simplemente cambie la parte superior del código a esto:

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*/
Ichibán
fuente
3
Gracias por tomarse el tiempo para verificar.
brianng
1
Hola Ichiban, Se compila y en realidad envía el correo electrónico con el archivo adjunto, pero el documento pdf adjunto tiene 0kb. ¿De verdad abriste el pdf que te envió un correo electrónico?
Gus Cavalcanti
2
@Gustavo, el archivo se abre correctamente en el visor de Acrobat. Tiene aproximadamente 900 bytes. Asegúrese de mantener la línea: memoryStream.Position = 0; justo después de doc.Close (). Me olvidé de mencionar eso. (ver actualización arriba)
ichiban
1
¡SI! Muchas gracias chicos. Finalmente funcionó. Dado que la respuesta de Ichiban se basó en la de brianng, creo que es justo marcar la respuesta de brianng como correcta.
Gus Cavalcanti
3

¿Puede vaciar el documento o el flujo de memoria y luego cerrarlo después de adjuntarlo?

James Conigliaro
fuente
Hola James. Hice esto y el resultado no cambió; sigo recibiendo el error "No se puede acceder a una transmisión cerrada". :( ¿Otras ideas?
Gus Cavalcanti
3

Probablemente llamando a doc.Close () Elimina la secuencia subyacente. Intente eliminar doc.Close () y en lugar de esa línea configure memoryStream.Position = 0;

Alternativamente, puede usar un archivo temporal:

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
fuente
huseyint, hice lo que sugirió y se envía el archivo pdf, pero solo tiene 15 bytes de longitud. Cuando intento abrirlo, está dañado. Siento que casi he llegado con tu sugerencia. ¿Alguna otra idea? ¡Gracias!
Gus Cavalcanti
Luego intente memoryStream.Flush (); antes de establecer la posición
huseyint
La misma cosa. El archivo se envía casi vacío y dañado. :(
Gus Cavalcanti
¿Le diste una oportunidad a "Crear un archivo temporal"?
huseyint
Estoy trabajando en ello ahora mismo y te lo haré saber en breve. ¡Gracias!
Gus Cavalcanti
1

Tuve el mismo problema y usé esta publicación para resolverlo.En el 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"));

Pienso en lugar de escribir

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

Simplemente crea una nueva transmisión

MemoryStream m = new MemoryStream(memoryStream);

y luego llamar

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

Ambos funcionan, pero creo que es mejor crear la nueva transmisión.

Zein Sleiman
fuente
¿Por qué es mejor crear una nueva transmisión?
Andy
No es. Es una pérdida de memoria y tiempo de CPU porque los bytes deben copiarse de uno a otro.
Serguei Fedorov
No recuerdo por qué dije que era mejor. Creo que podría querer decir que es más claro. Lo siento, acabo de ver esto. Ha
pasado