【问题标题】:iTextSharp Out of Memory Exception when stamping over ~1500 pages标记超过 1500 页时出现 iTextSharp 内存不足异常
【发布时间】:2014-07-17 20:00:22
【问题描述】:

我正在尝试从大约 5000 个表单的列表中创建单个 pdf 文档,这些表单是我从用 C# 编写的 ASP.NET MVC 网站中的对象列表中标记的。一旦我执行了大约 1500 个表格的冲压过程,我就会收到内存不足异常。如果我将对象列表限制为 1500 个以下对象,我不会收到错误消息。

这是模仿表单数据的数据对象:

public class MyPageData
{
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal DueAmount { get; set; }
    public decimal PaidAmount { get; set; }
}

创建文档的代码。注意 模板是一个 pdf 表单,Stream 是一个 FileStream 对象,创建时具有写访问权限,没有文件共享。

public class DocumentBuilder
{
    public static void FillDocument(IList<MyPageData> pages, string template, Stream outputFile)
    {
        using (Document document = new Document())
        {
            using (PdfSmartCopy copy = new PdfSmartCopy(document, outputFile))
            {
                document.Open();
                foreach (var page in pages)
                {
                    PdfReader reader = new PdfReader(StampPage(template, page));
                    copy.AddPage(copy.GetImportedPage(reader, 1));
                    copy.FreeReader(reader);
                }
            }
        }
    }

    private static byte[] StampPage(string template, MyPageData page)
    {
        PdfReader reader = new PdfReader(template);
        using (MemoryStream stream = new MemoryStream())
        {
            using (PdfStamper stamper = new PdfStamper(reader, stream))
            {
                AcroFields fields = stamper.AcroFields;
                FillFields(fields, page);
                stamper.FormFlattening = true;
                stamper.Writer.Flush();
            }
            return stream.ToArray();
        }
    }

    private static void FillFields(AcroFields fields, MyPageData page)
    {
        fields.SetField("Name", page.Name);
        fields.SetField("Description", page.Description);
        fields.SetField("DueAmount", page.DueAmount.ToString());
        fields.SetField("PaidAmout", page.PaidAmount.ToString());
    }
}

我浏览过 iText 和 iTextSharp 文档,我相信我的代码是正确的。我确实尝试压缩文档,如Here 演示的那样,但是在盖章时仍然会抛出错误。有什么想法或建议吗?

【问题讨论】:

  • 我知道发生了什么。其他开发人员之一签入了副本中没有的代码。FreeReader(reader);

标签: asp.net-mvc c#-4.0 itextsharp


【解决方案1】:

(还没有答案)

我刚刚使用以下代码尝试了您的代码,运行 5,000 对我来说没有问题。第一个块创建一个模板来匹配您的模板,第二个块根据您的类填充一些示例数据库,第三个块实际实现您的逻辑。你的代码和这个有什么不同吗?这会给您带来 OOM 异常吗?

//Creates our sample template
var templateFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "template.pdf");
using (var fs = new FileStream(templateFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
    using (var doc = new Document()) {
        using (var writer = PdfWriter.GetInstance(doc, fs)) {
            doc.Open();

            var fields = new string[] { "Name", "Description", "DueAmount", "PaidAmout" };
            var y = 700;
            foreach (var f in fields) {
                var tf = new TextField(writer, new iTextSharp.text.Rectangle(100, y, 400, y + 50), f);
                writer.AddAnnotation(tf.GetTextField());
                y -= 200;
            }

            doc.Close();
        }
    }
}

//Create our final PDF
var pages = new List<MyPageData>();
for (decimal i = 0; i < 5000; i++) {
    pages.Add(new MyPageData() { Name = "Chris", Description = "Cheese", DueAmount = i, PaidAmount = i * 0.5m });
}

//Create our file
var outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "output.pdf");
using (var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
    DocumentBuilder.FillDocument(pages, templateFile, fs);
}

编辑

我将其增加到 75 个字段,尽管需要更长的时间,但我仍然没有得到 OOM。为了测试,我将您的 MyPageData 类更改为:

public class MyPageData {
    public string[] StringData { get; set; }
}

还有你的DocumentBuilderFillFields 到:

private static void FillFields(AcroFields fields, MyPageData page) {
    var fieldCount = 75;
    for (var j = 0; j < fieldCount; j++) {
        fields.SetField("Field_" + j.ToString(), page.StringData[j]);
    }
}

我的模板和示例数据创建到:

var fieldCount = 75;

//Creates our sample template
var templateFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "template.pdf");
using (var fs = new FileStream(templateFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
    using (var doc = new Document()) {
        using (var writer = PdfWriter.GetInstance(doc, fs)) {
            doc.Open();

            var y = 800;
            //Create 75 fields
            for (var j = 0; j < fieldCount; j++) {
                var tf = new TextField(writer, new iTextSharp.text.Rectangle(100, y, 400, y + 10), "Field_" + j.ToString());
                tf.Options = tf.Options | TextField.MULTILINE;
                writer.AddAnnotation(tf.GetTextField());
                y -= 10;
            }

            doc.Close();
        }
    }
}

//Create our sample data
var pages = new List<MyPageData>();
for (decimal i = 0; i < 5000; i++) {
    var stringData = new List<string>();
    for (var j = 0; j < fieldCount; j++) {
        stringData.Add("Data_" + j.ToString());
    }
    pages.Add(new MyPageData() { StringData = stringData.ToArray() });
}

【讨论】:

  • 克里斯需要注意的一点是,我的“真实”页面日期包含 13 个字符串属性和 5 个十进制属性。此外,空模板大小为 148KB,包含 4 个多行字段。我不确定这是否会有所作为。但是,我可以使用您的代码生成模板以进行测试,看看是否仍然出现错误。我会让你知道结果。
  • 我在上面创建了第二个示例,它使用了 75 个字段。多线并不重要,但我还是设置了它。字符串与小数也不重要,因为它们都被转换为字符串,而小数无论如何只有 4 个字节。由于我很懒,我不得不更改您发布的一些代码以遍历字段而不是按名称使用它们。尽管运行时间更长,但仍然没有 OOM 异常。您使用的是 5.5.1 吗?
  • 我使用的是 5.5.1。奇怪的是,我使用您的模板创建代码运行它没有问题,然后切换回我的模板,它运行没有打嗝。我想我会密切关注事情,看看会发生什么。
  • 我只是用我的模板多次运行我的代码,没有出现任何问题。我不确定我的模板或输出文件是否处于奇怪状态,并且使用您的代码生成的模板释放了资源,但事情似乎工作正常。我怀疑我们可以将此标记为已回答并关闭。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 1970-01-01
  • 2015-06-07
  • 2020-07-22
相关资源
最近更新 更多