将byte []数组的一部分复制到PDFReader中

时间:2012-02-02 20:25:16

标签: c# asp.net itextsharp out-of-memory

这是继续努力减少我的记忆负荷 How do you refill a byte array using SqlDataReader?

所以我有一个设置大小的字节数组,对于这个例子,我会说新字节[400000]。在这个数组中,我将放置不同大小的pdf(小于400000)。

psuedo代码将是:

public void Run()
{
    byte[] fileRetrievedFromDatabase = new byte[400000];
    foreach (var document in documentArray)
    {
        // Refill the file with data from the database
        var currentDocumentSize = PopulateFileWithPDFDataFromDatabase(fileRetrievedFromDatabase);

        var reader = new iTextSharp.text.pdf.PdfReader(fileRetrievedFromDatabase.Take((int)currentDocumentSize ).ToArray());
        pageCount = reader.NumberOfPages;
        // DO ADDITIONAL WORK
    } 
}

private int PopulateFileWithPDFDataFromDatabase(byte[] fileRetrievedFromDatabase)
{
    // DataAccessCode Goes here
    int documentSize = 0;
    int bufferSize = 100;                   // Size of the BLOB buffer.
    byte[] outbyte = new byte[bufferSize];  // The BLOB byte[] buffer to be filled by GetBytes.

    myReader = logoCMD.ExecuteReader(CommandBehavior.SequentialAccess);

    Array.Clear(fileRetrievedFromDatabase, 0, fileRetrievedFromDatabase.Length);

    if (myReader == null)
    {
        return;
    }

    while (myReader.Read())
    {
        documentSize = myReader.GetBytes(0, 0, null, 0, 0);

        // Reset the starting byte for the new BLOB.
        startIndex = 0;

        // Read the bytes into outbyte[] and retain the number of bytes returned.
        retval = myReader.GetBytes(0, startIndex, outbyte, 0, bufferSize);

        // Continue reading and writing while there are bytes beyond the size of the buffer.
        while (retval == bufferSize)
        {
            Array.Copy(outbyte, 0, fileRetrievedFromDatabase, startIndex, retval);

            // Reposition the start index to the end of the last buffer and fill the buffer.
            startIndex += retval;
            retval = myReader.GetBytes(0, startIndex, outbyte, 0, bufferSize);
        }
    }

    return documentSize;
}

以上代码的问题在于,当我尝试访问PDF阅读器时,我不断收到“未找到重建预告片。原始错误:未找到PDF startxref”错误。我相信这是因为字节数组太长并且尾随0。但由于我正在使用字节数组,因此我不会在LOH上不断构建新对象,我需要这样做。

那么我如何获得我需要的数组并将其发送到PDFReader呢?

更新

所以我查看了源代码,发现我的实际代码中有一些变量令人困惑。我基本上在循环的每次迭代中重用fileRetrievedFromDatabase对象。由于它通过引用传递,它被清除(设置为全零),然后填充PopulateFileWithPDFDataFromDatabase。然后,此对象用于创建新PDF。

如果我没有这样做,将在每次迭代中创建一个新的大字节数组,并且Large Object Heap会变满并最终抛出OutOfMemory异常。

2 个答案:

答案 0 :(得分:1)

您至少有两个选择:

  1. 将缓冲区视为circular buffer,其中包含两个索引,用于开始和结束位置。 需要一个用outByte写的最后一个字节的索引,当你到达那个索引时你必须停止阅读。
  2. 只需读取与data数组中相同的字节数,以避免读入缓冲区中不属于同一文件的“未知”部分。
  3. 换句话说,不要将bufferSize作为最后一个参数,而是data.Length

    // Read the bytes into outbyte[] and retain the number of bytes returned.
    retval = myReader.GetBytes(0, startIndex, outbyte, 0, data.Length);
    

    如果data长度为10且您的outbyte缓冲区为15,那么您应该只阅读data.Length而不是bufferSize

    然而,我仍然没有看到你如何重复使用outbyte“缓冲区”,如果这就是你正在做的......我根本不按照你在答案中提供的内容进行跟踪。也许你可以准确地澄清什么是重复利用。

答案 1 :(得分:1)

显然,我是while循环当前结构化的方式,它没有复制数据的最后一次迭代。需要添加这个:

if (outbyte != null && outbyte.Length > 0 && retval > 0)
{
    Array.Copy(outbyte, 0, currentDocument.Data, startIndex, retval);
}

它现在正在工作,虽然我肯定需要重构。