【问题标题】:Merging multiple PDFs to a single very large PDF with PDFSharp without running out of memory使用 PDFSharp 将多个 PDF 合并为一个非常大的 PDF 而不会耗尽内存
【发布时间】:2021-06-07 21:14:05
【问题描述】:

我正在使用 PDFsharp 将大量文件(存储在磁盘上)合并为一个 PDF。有时 PDF 可以大到 700MB。我正在使用提供的示例代码,基本上创建一个输出PdfDocument,向其中添加页面,然后调用 outputDocument.Save(destinationPath),因此使用的内存量与生成的文档大小大致相同。

有没有办法将更改流式传输到文件以避免内存消耗?如果没有,有没有办法利用 MigraDoc 做到这一点?

更新
根据评论中的建议,我整理了一些代码来关闭和重新打开文档,虽然内存使用受到控制并且文件确实增长了,但它似乎并没有附加页面。如果我将“路径”设置为包含 3000 个单页文件的列表,我仍然会得到一个 500 页的文档。代码如下:

var destinationFile = "c:\\test.pdf";
var directory = Path.GetDirectoryName(destinationFile);

if (!Directory.Exists(directory))
{
    Directory.CreateDirectory(directory);
}

var fs = new FileStream(destinationFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
var outputDocument = new PdfDocument(fs);
var count = 0;

// Iterate files (paths is a List<string> collection)
foreach (string path in paths)
{
    var inputDocument = PdfReader.Open(path, PdfDocumentOpenMode.Import);

    // Iterate pages
    for (int idx = 0; idx < inputDocument.PageCount; idx++)
    {
        // Get the page from the external document...
        PdfPage page = inputDocument.Pages[idx];
        // ...and add it to the output document.
        outputDocument.AddPage(page);
    }

    inputDocument.Dispose();
    
    count++;
    if (count % 500 == 0 || count == paths.Count)
    {
        outputDocument.Close();
        fs.Close();
        fs.Dispose();

        if (count < paths.Count)
        {
            fs = new FileStream(destinationFile, FileMode.Append, FileAccess.Write);
            outputDocument = new PdfDocument(fs);
        }
    }
}

更新 2
这是一些使用 PDFReader 关闭和重新打开文档的新代码。程序正在合并 2000 个 4 页 140KB PDF,输出文件为 273MB。我试过没有关闭和重新打开,关闭和重新打开每 1000、500、250 和 100 个文件。结果如下:

无间隔:21 秒,最大内存 330MB 1000 间隔:30 秒,最大内存 490MB 500 间隔:55 秒,最大内存 710MB 250间隔:1分35秒,最大内存780MB 100间隔:2分55秒,最大内存850mb

class Program
{
    public static void Main(string[] args)
    {
        var files = new List<string>();
        var basePath = AppDomain.CurrentDomain.BaseDirectory;

        for (var i = 0; i < 2000; i++)
        {
            files.Add($"{basePath}\\sample.pdf");
        }
        DoMerge(files, $"{basePath}\\output.pdf");
    }

    private static void DoMerge(List<string> paths, string destinationFile)
    {

        var directory = Path.GetDirectoryName(destinationFile);

        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }

        var outputDocument = new PdfDocument();
        var count = 0;

        // Iterate files
        foreach (string path in paths)
        {
            // Open the document to import pages from it.
            try
            {
                var inputDocument = PdfReader.Open(path, PdfDocumentOpenMode.Import);

                // Iterate pages
                for (int idx = 0; idx < inputDocument.PageCount; idx++)
                {
                    // Get the page from the external document...
                    PdfPage page = inputDocument.Pages[idx];
                    // ...and add it to the output document.
                    outputDocument.AddPage(page);
                }

                inputDocument.Dispose();
                
                count++;
                if (count % 500 == 0 || count == paths.Count)
                {
                    outputDocument.Save(destinationFile);
                    outputDocument.Close();
                    outputDocument.Dispose();

                    if (count < paths.Count)
                    {
                        outputDocument = PdfReader.Open(destinationFile, PdfDocumentOpenMode.Import);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
        }
    }
}

【问题讨论】:

  • 您必须使用PdfReader.Open 打开输出文档以添加更多页面。
  • @IlikedtheoldStackOverflow 我也尝试过,同时使用 PdfDocumentOpenMode.Modify 和 PdfDocumentOpenMode.Import。内存使用永远不会下降。老实说,我不希望它 PdfReader.Open() 将整个文档加载到内存中。所以如果文档不断增长,每次我打开它都会占用更多内存,不是吗?
  • PDFsharp 在使用时会初始化一些成员。新添加的页面比打开文档中的“空闲”页面需要更多内存。关闭并重新打开目标文件是一种行之有效的技巧。但是在 32 位模式下运行时,限制仍然是 2 GiB。可能500个文件的间隔太长了。

标签: c# pdfsharp


【解决方案1】:

为了减少内存占用,您可以不时关闭目标文件,然后再次打开它,并在其中添加更多 PDF 文件。
PDFsharp 不支持将数据交换到文件。

确保您的应用在 64 位模式下运行,以允许其使用超过 2 GiB 的 RAM。

【讨论】:

  • 我试过这个,用代码更新了我的答案,但它的行为不像预期的那样。知道我做错了什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-25
  • 2013-03-03
  • 2015-06-07
  • 2013-06-16
  • 1970-01-01
  • 2013-06-10
相关资源
最近更新 更多