在生成PDF时无法访问已关闭的流

9
当我使用iTextSharp创建多个表格的PDF文件时,出现以下错误消息:

无法访问已关闭的流。

这是我的代码:
//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
"bytes"数组应该在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);

但我仍然收到相同的错误提示。

1
尝试将您的 foreach 移动到 using (PdfWriter writer... 内部。 - Chris Haas
我尝试将foreach循环移动到using(writer...)块内部:- 然后呢?有什么变化吗?我假设仍然会出现一些错误,请尝试将bytes = ms.ToArray();doc.Close();这两行代码互换。 - mkl
@mkl 很抱歉,我忘记告诉你它仍然会出现相同的错误。问题已更新。交换这两个语句仍然没有改变任何东西。无法访问已关闭的流。 - Hack4Life
我刚试图重现你的问题。显然,我不得不删除所有与你的自定义数据结构相关的代码,而不是使用你的foreach循环,我使用了一个简单的for(int dummy = 1; dummy <5; dummy ++),而不是检索和添加一些数据表结构,我只是添加了new Paragraph(dummy.ToString())。我也遇到了一个异常,这个异常可以通过不将PdfWriter放入using语句中来消除。 - mkl
但是,我得到了与您发布的异常不同的异常。因此,我认为在您的情况下,实际上会在检索和添加某些数据表结构的代码中引发一些异常(当通过“using”块向下移动时),从而触发一些后续问题。因此,我建议您首先使用类似于我的虚拟代码使一切正常,只有在那样做成功后,再引入您的数据检索和表格构建代码。 - mkl
显示剩余2条评论
6个回答

20

默认情况下,PdfWriter会关闭流。只需在PdfWriter.GetInstance之后添加以下行:

writer.CloseStream = false;

我尝试了一下,抛出了以下异常:“集合已被修改;枚举操作可能无法执行。” 我在开括号后添加了 writer.CloseStream = false; - Hack4Life
我做了这个,它有效。我还在代码块底部添加了一个writer.close()来手动关闭它,尽管这可能会在子程序结束时自动清理,但我想保险起见。 - Taylor Brown
在添加了这行代码之后,我得到了实际的错误抛出,谢谢。 - Harish Nayak

1
解决方案很简单,只需在foreach循环中将我的集合添加.ToList()即可:
foreach (TableObject to in myTables.ToList())
{
     //some code stuff

这个答案这个问题下帮助我解决了这个问题。


0

使用 PdfWriter writer.. 上的 using 块时,有可能在尝试处理 writer 时关闭底层流 ms

尝试移除不带 using 块的 PdfWriter writer,看看是否解决了问题。


0

我在使用xmlworker(iTextSharp html to pdf)时遇到了"Cannot acces a closed stream"的问题,当我有一个没有行的空表格时:

 <table>
    @if (false) 
    {
       <tr>
           <td>Foo</td>
       </tr>
    }
 </table>

0
使用using命令会导致您的MemoryStream被释放,因此当您访问它时,托管和非托管资源已经被释放。请在foreach循环的右括号后面添加以下代码:
bytes = ms.ToArray();

请更新代码以展示你尝试过的内容。你有尝试过删除 "using" 部分吗? - user8128167

-1
问题似乎是由于在关闭文档之前,PdfWriter已被释放。在 PdfWriter 的 using 语句内调用 doc.Close() 将应该解决这个问题。

不是和是。主要问题在于已接受答案中解决的那个问题,因为它引发了一个异常,然后被后续异常隐藏了。除此之外,代码在使用iText API时包含了许多错误,其中一个就是你指出的。 - mkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接