首页 > 解决方案 > 使用 PDFSharp 将多个 PDF 合并为一个非常大的 PDF 而不会耗尽内存

问题描述

我正在使用 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);
            }
        }
    }
}

标签: c#pdfsharp

解决方案


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

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


推荐阅读