Cannot access a closed stream in pdf generation

10,487

Solution 1

The solution was simple, just putting a .ToList() to my collection in the foreach loop:

foreach (TableObject to in myTables.ToList())
{
     //some code stuff

This answer under this question helped me to solve this problem.

Solution 2

PdfWriter closes the stream by default. Just add the following line after PdfWriter.GetInstance

writer.CloseStream = false;
Share:
10,487
Hack4Life
Author by

Hack4Life

Basically I live on the internet. But sometimes I also live in Austria. I love photography, I programm Apps with Xamarin.IOS and have the most beautiful girlfriend. I'm also the founder and CEO of Hack4Life, photographer/videographer of UnnamedProduction, coder [web/iOS] of Alpscapes(R) and many more sites.

Updated on June 09, 2022

Comments

  • Hack4Life
    Hack4Life about 2 years

    I get the following error message when I try to build a PDF file using iTextSharp with multiple tables:

    Cannot access a closed Stream.

    Here is my code:

    //Create a byte array that will eventually hold our final PDF
    Byte[] bytes;
    
    List<TableObject> myTables = getTables();
    TableObject currentTable = new TableObject();
    
    //Boilerplate iTextSharp setup here
    //Create a stream that we can write to, in this case a MemoryStream
    using (MemoryStream ms = new MemoryStream())
    {
        //Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
        using (Document doc = new Document(PageSize.A4, 10f, 10f, 10f, 0f))
        {
            foreach (TableObject to in myTables)
            {
                //Create a writer that's bound to our PDF abstraction and our stream
                using (PdfWriter writer = PdfWriter.GetInstance(doc, ms))
                {
                    if (!doc.IsOpen())
                    {
                        //Open the document for writing
                        doc.Open();
                    }
                    //Get the data from database corresponding to the current tableobject and fill all the stuff we need!
                    DataTable dt = getDTFromID(to._tableID);
                    Object[] genObjects = new Object[5];
                    genObjects = gen.generateTable(dt, currentTable._tableName, currentTable._tableID.ToString(), currentTable, true);
    
                    StringBuilder sb = (StringBuilder)genObjects[1];
                    String tableName = sb.ToString();
                    Table myGenTable = (Table)genObjects[0];
                    String table = genObjects[2].ToString();
    
                    using (StringReader srHtml = new StringReader(table))
                    {
                        //Parse the HTMLiTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
                    }
    
                    //should give empty page at the end, need to fix it later
                    doc.NewPage();
                }
    
            }
            doc.Close();
        }
    
        //After all of the PDF "stuff" above is done and closed but **before** we
        //close the MemoryStream, grab all of the active bytes from the stream
        bytes = ms.ToArray();
    }
    
    //Now we just need to do something with those bytes.
    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-Disposition", "attachment; filename=Report_complete.pdf");
    Response.BinaryWrite(bytes);
    

    Here is the complete stacktrace from my asp.net application:

    [ObjectDisposedException: Cannot access a closed Stream.]
    System.IO.__Error.StreamIsClosed() +57
    System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) +11011171 iTextSharp.text.pdf.OutputStreamCounter.Write(Byte[] buffer, Int32 offset, Int32 count) +52
    iTextSharp.text.pdf.PdfIndirectObject.WriteTo(Stream os) +53
    iTextSharp.text.pdf.PdfBody.Write(PdfIndirectObject indirect, Int32 refNumber, Int32 generation) +100
    iTextSharp.text.pdf.PdfBody.Add(PdfObject objecta, Int32 refNumber, Int32 generation, Boolean inObjStm) +385
    iTextSharp.text.pdf.PdfWriter.AddToBody(PdfObject objecta, PdfIndirectReference refa) +51
    iTextSharp.text.pdf.Type1Font.WriteFont(PdfWriter writer, PdfIndirectReference piref, Object[] parms) +317
    iTextSharp.text.pdf.FontDetails.WriteFont(PdfWriter writer) +296
    iTextSharp.text.pdf.PdfWriter.AddSharedObjectsToBody() +180
    iTextSharp.text.pdf.PdfWriter.Close() +86
    iTextSharp.text.DocWriter.Dispose() +10
    System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +51 System.Web.UI.Control.OnLoad(EventArgs e) +92
    System.Web.UI.Control.LoadRecursive() +54
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +772

    The bytes-Array should be accessable inside the using statements, but it looks like that there is the error.

    I've tried moving the foreach loop inside the using(writer ...) block:

    //Create a byte array that will eventually hold our final PDF
    //must be outside of the foreach loop (and everything else), because we store every single generated table in here for the final pdf!!
    Byte[] bytes;
    
    List<TableObject> myTables = getTables();
    TableObject currentTable = new TableObject();
    
    //Boilerplate iTextSharp setup here
    //Create a stream that we can write to, in this case a MemoryStream
    using (MemoryStream ms = new MemoryStream())
    {
        //Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
        using (Document doc = new Document(PageSize.A4, 10f, 10f, 10f, 0f))
        {
                //Create a writer that's bound to our PDF abstraction and our stream
                using (PdfWriter writer = PdfWriter.GetInstance(doc, ms))
                {
                    //loop all tableobjects inside the document & the instance of PDFWriter itself! 
                    foreach (TableObject to in myTables)
                    {
                        //only happens on the first run!
                        if (!doc.IsOpen())
                        {
                            //Open the document for writing
                            doc.Open();
                        }
                        //Get the data from database corresponding to the current tableobject and fill all the stuff we need!
                        DataTable dt = getDTFromID(to._tableID);
                        Object[] genObjects = new Object[5];
                        genObjects = gen.generateTable(dt, currentTable._tableName, currentTable._tableID.ToString(), currentTable, true);
    
                        StringBuilder sb = (StringBuilder)genObjects[1];
                        String tableName = sb.ToString();
                        Table myGenTable = (Table)genObjects[0];
                        String table = genObjects[2].ToString();
    
                        using (StringReader srHtml = new StringReader(table))
                        {
                            //Parse the HTML
                            iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
                        }
    
                        //this will probably render a whole new page at the end of the file!! need to be fixed later!!!
                        doc.NewPage();
                    }
                    //After all of the PDF "stuff" above is done and closed but **before** we
                    //close the MemoryStream, grab all of the active bytes from the stream
                    bytes = ms.ToArray();
                }
            doc.Close();
        }
    
    
    }
    
    //Now we just need to do something with those bytes.
    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-Disposition", "attachment; filename=ShiftReport_complete.pdf");
    Response.BinaryWrite(bytes);
    

    But I still get the same error.