无法在 pdf 生成中访问已关闭的流

Cannot access a closed stream in pdf generation

当我尝试使用包含多个表格的 iTextSharp 构建 PDF 文件时收到以下错误消息:

Cannot access a closed Stream.

这是我的代码:

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

这是我的 asp.net 应用程序的完整堆栈跟踪:

[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

bytes-Array 应该可以在 using 语句中访问,但看起来有错误。

我尝试将 foreach 循环移动到 using(writer ...) 块中:

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

但我仍然得到同样的错误。

PdfWriter writer.. 上的 using 块可能会在尝试处理 writer 时关闭基础流 ms

尝试在不使用 using 块的情况下使用 PdfWriter writer 删除并查看。它应该可以解决问题。

using 命令导致您的 MemoryStream 被释放,因此当您访问它时,托管和非托管资源已经被释放。将此代码放在 foreach 循环的右括号之后:

bytes = ms.ToArray();

PdfWriter 默认关闭流。只需在 PdfWriter.GetInstance

之后添加以下行
writer.CloseStream = false;

解决方案很简单,只需在 foreach 循环中将 .ToList() 放入我的 collection 即可:

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

This answer under this question帮我解决了这个问题。

问题似乎是由于 PdfWriter 在文档关闭前被处理所致。在 PdfWriter 的 using 语句中调用 doc.Close() 应该可以解决问题。