跳至页脚内容
使用IRONPDF

如何在 C# 中合并 PDF 字节数组

从您的字节数组中创建PdfDocument.Merge()将它们合并为一个PDF文件而无需保存到磁盘。这种方法自动处理复杂的PDF结构,允许您合并存储在数据库中的文档或通过API接收到的文档,而无需编写临时文件。

在现代C#应用中,处理存储为字节数组的PDF文件是很常见的。 无论您是从数据库中检索PDF文档,从Web服务接收它们,还是在内存中处理,能够合并多个PDF文件而不保存到磁盘对于企业级工作流至关重要。 IronPDF使用直观的API让这一过程简单明了。 在本文中,您将学习如何在C#中合并PDF字节数组,并探索包括MemoryStream处理和真实数据库模式在内的不同方法。

什么是PDF字节数组,为什么要合并它们?

字节数组是在内存中表示PDF文件的原始二进制数据。 在C#中处理PDF文档时,您会经常遇到文件以字节数组而不是磁盘上的形式存在的情况。 这种情况尤其常见于从将PDF存储为二进制列的数据库中检索文档,或从REST API接收文档时。

.NET中的MemoryStream功能--在Microsoft MemoryStream参考中有所描述--使处理这些字节数组更为高效,尤其是结合对大文档进行适当的内存管理时。 您可以在完全内存中加载、处理和保存PDF文件,而不需要写入临时文件--这样更快、更干净,并避免了文件系统权限问题。

为什么不能简单地连接PDF字节数组?

简单地连接两个PDF字节数组是行不通的。 与纯文本文件不同,PDF文件具有复杂的内部结构,包括头部、交叉引用表和特定的格式约定。 ISO 32000 PDF规范定义了关于文档结构的复杂规则,包括元数据、字体嵌入和加密层。 直接拼接字节会产生损坏的文件。您需要一个合适的PDF库来解析这些字节数组并正确地合并它们,同时保留所有结构完整性。

IronPDF内部处理了所有这些复杂性。 您只需几行代码就可以合并PDF文档,同时精确保留源文件中的字体、图像和格式。

什么时候应该使用字节数组合并?

这种方法在以下情况下效果很好:

  • 文档存储在SQL Server或PostgreSQL数据库中作为二进制列
  • 您的应用程序从外部API或微服务接收PDF数据
  • 您在ASP.NET中处理文件上传而不将它们保存到磁盘
  • 您在Azure Functions或AWS Lambda等云环境中运行,那里限制了临时文件存储

与Azure Blob Storage或类似的云服务一起工作时,字节数组操作变得至关重要,因为您下载原始字节、处理它们并上传结果--这一切都无需触碰文件系统。

如何将PDF库添加到您的项目?

入门需要将IronPDF NuGet包添加到您的项目中。 该包可以在NuGet.org上获取。 您可以使用包管理器控制台或.NET CLI进行安装:

Install-Package IronPdf
Install-Package IronPdf
SHELL
dotnet add package IronPdf
dotnet add package IronPdf
SHELL

有关包括Docker部署Linux设置的详细安装选项,请参阅高级安装指南。 如果您部署到一个精简环境,IronPDF Slim可以显著减少部署足迹。

安装后,在您的C#文件顶部添加以下命名空间:

using IronPdf;
using System.IO;
using System.Collections.Generic;
using IronPdf;
using System.IO;
using System.Collections.Generic;
$vbLabelText   $csharpLabel

IronPDF支持Windows、macOS和Linux平台。 它与ASP.NET Core、Blazor、控制台应用程序和云环境集成,不需要额外的配置。

Visual Studio的NuGet包管理器界面显示IronPDF库搜索结果,选择安装版本2025.9.4到IronTesting项目中——显著显示安装按钮和版本下拉菜单

立即开始使用 IronPDF。
green arrow pointer

如何在C#中合并两个PDF字节数组?

这里有一个完整的示例展示了如何将两个PDF字节数组合并成一个PDF文档:

// Simulate two PDF byte arrays (in practice, these come from a database or API)
byte[] pdfBytes1 = File.ReadAllBytes("document1.pdf");
byte[] pdfBytes2 = File.ReadAllBytes("document2.pdf");

// Create PdfDocument objects from byte arrays
var pdf1 = new PdfDocument(pdfBytes1);
var pdf2 = new PdfDocument(pdfBytes2);

// Merge the two PDF documents
PdfDocument combinedPdf = PdfDocument.Merge(pdf1, pdf2);

// Convert the combined PDF back to a byte array
byte[] mergedPdfBytes = combinedPdf.BinaryData;

// Optionally save the merged PDF to disk
File.WriteAllBytes("merged.pdf", mergedPdfBytes);
// Simulate two PDF byte arrays (in practice, these come from a database or API)
byte[] pdfBytes1 = File.ReadAllBytes("document1.pdf");
byte[] pdfBytes2 = File.ReadAllBytes("document2.pdf");

// Create PdfDocument objects from byte arrays
var pdf1 = new PdfDocument(pdfBytes1);
var pdf2 = new PdfDocument(pdfBytes2);

// Merge the two PDF documents
PdfDocument combinedPdf = PdfDocument.Merge(pdf1, pdf2);

// Convert the combined PDF back to a byte array
byte[] mergedPdfBytes = combinedPdf.BinaryData;

// Optionally save the merged PDF to disk
File.WriteAllBytes("merged.pdf", mergedPdfBytes);
$vbLabelText   $csharpLabel

PdfDocument类在其构造函数中直接接受原始字节数组。 当您有两个PdfDocument.Merge()将它们合并成一个文档。 然后BinaryData属性将结果以字节数组的形式返回,准备存回数据库或通过API传输。

PdfDocument API提供了广泛的功能,除了简单的合并,还有页面操作文本提取表单处理。 一旦您有了合并后的文档,可以在提取最终字节数组之前应用这些操作中的任何一个。

合并后的输出是什么样的?

PDF查看器成功显示合并后的PDF文档,第一页为'PDF One',第二页为'PDF Two',展示了清晰的文档边界和在100%缩放下保持的格式

输出是一个包含两个源文档中所有页面的单个PDF,顺序为通过Merge()传递时的顺序。 页码、字体、图像和嵌入内容都得到了保留。 合并后的文档与任何其他PDF行为一致--您可以使用IronPDF方法对其分页、注释、签名或压缩,如同对任何文档一样。

合并过程内部是如何工作的?

当您将字节数组传递给PdfDocument构造函数时,IronPDF解析二进制数据并构建PDF结构的内存表达。 然后PdfDocument.Merge()方法通过依次追加每个源的页面、重建交叉引用表以及解决文档之间任何字体或资源名称冲突来合并多个文档。

这就是为什么您不能简单地连接字节数组的原因--第一个PDF中的交叉引用表指向该文件内的偏移量。拼接后,这些偏移量因为第二个文件而无效了。 IronPDF正确地重建了整个结构,生成一个有效且格式良好的PDF。

如何一次合并多于两个PDF文件?

IronPDF提供了一个List重载用于在一次操作中合并任意数量的文档。 这比链接多个两文档合并更高效:

// Load four PDFs as byte arrays
List<byte[]> pdfByteArrays = new List<byte[]>
{
    File.ReadAllBytes("example1.pdf"),
    File.ReadAllBytes("example2.pdf"),
    File.ReadAllBytes("example3.pdf"),
    File.ReadAllBytes("example4.pdf")
};

// Convert each byte array to a PdfDocument
List<PdfDocument> pdfsToMerge = new List<PdfDocument>();
for (int i = 0; i < pdfByteArrays.Count; i++)
{
    pdfsToMerge.Add(new PdfDocument(pdfByteArrays[i]));
}

// Merge all documents in one call
PdfDocument combinedPdf = PdfDocument.Merge(pdfsToMerge);
byte[] finalPdfBytes = combinedPdf.BinaryData;

// Apply compression if the result is large
if (finalPdfBytes.Length > 1024 * 1024 * 10) // 10 MB
{
    combinedPdf.CompressImages(90);
    finalPdfBytes = combinedPdf.BinaryData;
}
// Load four PDFs as byte arrays
List<byte[]> pdfByteArrays = new List<byte[]>
{
    File.ReadAllBytes("example1.pdf"),
    File.ReadAllBytes("example2.pdf"),
    File.ReadAllBytes("example3.pdf"),
    File.ReadAllBytes("example4.pdf")
};

// Convert each byte array to a PdfDocument
List<PdfDocument> pdfsToMerge = new List<PdfDocument>();
for (int i = 0; i < pdfByteArrays.Count; i++)
{
    pdfsToMerge.Add(new PdfDocument(pdfByteArrays[i]));
}

// Merge all documents in one call
PdfDocument combinedPdf = PdfDocument.Merge(pdfsToMerge);
byte[] finalPdfBytes = combinedPdf.BinaryData;

// Apply compression if the result is large
if (finalPdfBytes.Length > 1024 * 1024 * 10) // 10 MB
{
    combinedPdf.CompressImages(90);
    finalPdfBytes = combinedPdf.BinaryData;
}
$vbLabelText   $csharpLabel

这种方法可扩展到任意数量的文档。 每个PDF被加载到一个PdfDocument对象中,添加到列表中,然后在一次调用中合并。 对于大输出文件,PDF压缩在不丢失有意义质量的情况下减少最后的大小。

什么时候应该使用MemoryStream来合并PDF?

MemoryStream方法在与其他使用流而非字节数组的.NET库集成时为您提供更大的控制。 当您已经有一个可用的流时(例如,从HTTP响应或blob存储SDK中):

using (var stream1 = new MemoryStream(pdfBytes1))
using (var stream2 = new MemoryStream(pdfBytes2))
{
    var pdf1 = new PdfDocument(stream1);
    var pdf2 = new PdfDocument(stream2);

    var merged = PdfDocument.Merge(pdf1, pdf2);

    // Add metadata to the merged document
    merged.MetaData.Author = "Your Application";
    merged.MetaData.Title = "Merged Document";
    merged.MetaData.CreationDate = DateTime.Now;

    byte[] result = merged.BinaryData;
}
using (var stream1 = new MemoryStream(pdfBytes1))
using (var stream2 = new MemoryStream(pdfBytes2))
{
    var pdf1 = new PdfDocument(stream1);
    var pdf2 = new PdfDocument(stream2);

    var merged = PdfDocument.Merge(pdf1, pdf2);

    // Add metadata to the merged document
    merged.MetaData.Author = "Your Application";
    merged.MetaData.Title = "Merged Document";
    merged.MetaData.CreationDate = DateTime.Now;

    byte[] result = merged.BinaryData;
}
$vbLabelText   $csharpLabel

您可以在提取最终字节之前通过设置元数据添加水印应用数字签名来丰富合并后的文档。 对于合规场景,考虑PDF/A转换用于长期存档或PDF/UA合规性用于可访问性要求。

基于流的处理为较大PDF文件提供了更好的内存管理,并与云存储SDK干净集成。 这种方法对高吞吐量服务中的异步模式特别实用。

如何合并从数据库中检索的PDF?

一个常见的真实模式是在需要时从SQL数据库中获取PDF字节数组并将它们合并。 以下是一个具有错误处理的生产就绪示例:

public string MergePdfDocumentsFromDatabase(List<int> documentIds)
{
    List<PdfDocument> documents = new List<PdfDocument>();

    try
    {
        foreach (int id in documentIds)
        {
            // Fetch PDF byte array from database
            byte[] pdfData = GetPdfFromDatabase(id); // Replace with your data access logic

            if (pdfData == null || pdfData.Length == 0)
            {
                Console.WriteLine($"Warning: Document {id} is empty or not found");
                continue;
            }

            documents.Add(new PdfDocument(pdfData));
        }

        if (documents.Count == 0)
        {
            return "Error: No valid documents found to merge";
        }

        // Merge all documents
        PdfDocument mergedDocument = PdfDocument.Merge(documents);

        // Add page numbers to the footer
        mergedDocument.AddHtmlFooters(new HtmlHeaderFooter()
        {
            HtmlFragment = "<center>Page {page} of {total-pages}</center>",
            DrawDividerLine = true
        });

        // Save back to the database
        byte[] resultBytes = mergedDocument.BinaryData;
        SaveMergedPdfToDatabase(resultBytes);

        return "Document successfully combined and saved.";
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error merging PDFs: {ex.Message}");
        return $"Merge failed: {ex.Message}";
    }
}
public string MergePdfDocumentsFromDatabase(List<int> documentIds)
{
    List<PdfDocument> documents = new List<PdfDocument>();

    try
    {
        foreach (int id in documentIds)
        {
            // Fetch PDF byte array from database
            byte[] pdfData = GetPdfFromDatabase(id); // Replace with your data access logic

            if (pdfData == null || pdfData.Length == 0)
            {
                Console.WriteLine($"Warning: Document {id} is empty or not found");
                continue;
            }

            documents.Add(new PdfDocument(pdfData));
        }

        if (documents.Count == 0)
        {
            return "Error: No valid documents found to merge";
        }

        // Merge all documents
        PdfDocument mergedDocument = PdfDocument.Merge(documents);

        // Add page numbers to the footer
        mergedDocument.AddHtmlFooters(new HtmlHeaderFooter()
        {
            HtmlFragment = "<center>Page {page} of {total-pages}</center>",
            DrawDividerLine = true
        });

        // Save back to the database
        byte[] resultBytes = mergedDocument.BinaryData;
        SaveMergedPdfToDatabase(resultBytes);

        return "Document successfully combined and saved.";
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error merging PDFs: {ex.Message}");
        return $"Merge failed: {ex.Message}";
    }
}
$vbLabelText   $csharpLabel

这种模式通过跳过它们并继续处理有效文档来优雅地处理缺失或空记录。 合并结果通过HTML页眉/页脚添加页码后再写回数据库。 对于更高级的导航,您可以添加书签来帮助读者导航长文档。

是什么让数据库模式有效?

上述模式适用于发票、报告、合同或任何存储为二进制列的文档。 主要优势:

  • 没有临时文件:整个工作流程在内存中进行,这避免了文件系统访问并减少了攻击面。
  • 优雅跳过:无效或缺失的记录不会中止整个合并--它们被记录并跳过。
  • 保存前丰富:在提取最终字节数组之前为合并的文档添加页脚或元数据,因此结果是完整的并且可以立即使用。
  • 单次数据库写入:合并结果一次写入,使数据库事务简单。

如何处理错误和边缘情况?

最常见的错误场景是什么?

在构建PDF合并工作流时,多个故障模式值得加以提防:

  1. 空字节数组:最常见的问题。 在构造pdfData != null && pdfData.Length > 0
  2. 损坏或无效的PDF数据:如果字节数组在数据库存储或API传输期间被截断,构造函数将抛出异常。 捕获异常并记录文档ID。
  3. 无密码的加密PDF:尝试合并没有提供密码的密码保护的PDF将抛出异常。 使用IronPDF的密码保护PDF处理来提供凭证。
  4. 许多大文件带来的内存压力:同时加载大量大PDF会耗尽可用内存。 分批处理它们,并在合并后处理PdfDocument对象。

这是一种带有输入验证的可靠模式:

public bool TryMergePdfByteArrays(byte[] pdfBytes1, byte[] pdfBytes2, out byte[] mergedBytes)
{
    mergedBytes = null;

    try
    {
        if (pdfBytes1 == null || pdfBytes1.Length == 0)
            throw new ArgumentException("First PDF byte array is null or empty");

        if (pdfBytes2 == null || pdfBytes2.Length == 0)
            throw new ArgumentException("Second PDF byte array is null or empty");

        using var pdf1 = new PdfDocument(pdfBytes1);
        using var pdf2 = new PdfDocument(pdfBytes2);

        if (pdf1.PageCount == 0)
            throw new InvalidOperationException("First PDF has no pages");

        if (pdf2.PageCount == 0)
            throw new InvalidOperationException("Second PDF has no pages");

        var mergedPdf = PdfDocument.Merge(pdf1, pdf2);
        mergedBytes = mergedPdf.BinaryData;

        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"PDF merge failed: {ex.Message}");
        return false;
    }
}
public bool TryMergePdfByteArrays(byte[] pdfBytes1, byte[] pdfBytes2, out byte[] mergedBytes)
{
    mergedBytes = null;

    try
    {
        if (pdfBytes1 == null || pdfBytes1.Length == 0)
            throw new ArgumentException("First PDF byte array is null or empty");

        if (pdfBytes2 == null || pdfBytes2.Length == 0)
            throw new ArgumentException("Second PDF byte array is null or empty");

        using var pdf1 = new PdfDocument(pdfBytes1);
        using var pdf2 = new PdfDocument(pdfBytes2);

        if (pdf1.PageCount == 0)
            throw new InvalidOperationException("First PDF has no pages");

        if (pdf2.PageCount == 0)
            throw new InvalidOperationException("Second PDF has no pages");

        var mergedPdf = PdfDocument.Merge(pdf1, pdf2);
        mergedBytes = mergedPdf.BinaryData;

        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"PDF merge failed: {ex.Message}");
        return false;
    }
}
$vbLabelText   $csharpLabel

PdfDocument对象被正确处理,即使发生异常也能释放非托管资源。 TryXxx模式返回布尔成功指示器,而不是抛出异常,使其易于在处理多个文档的高级代码中调用。

如何防止常见错误?

一些习惯可以降低生产失败的风险:

  • 加载前验证:检查字节数组是否非空并具有一个可信的最小长度(PDF头至少有几百字节)。
  • 使用using进行处理:IronPDF文档持有本地资源。 始终处理它们,使用Dispose()调用。
  • 启用自定义日志记录:每次从数据库合并文档时记录文档ID、字节数组长度和页数。 这使生产问题调试变得更加容易。
  • 明确处理加密PDF:检查文档是否需要密码后再合并。 试图在无凭证时读取加密文档会抛出异常,而不是返回空白页。
  • 为复杂文档设置超时:非常大或复杂的PDF可能需要处理时间。 考虑异步操作和高容量场景的适当超时值。
PDF合并方法比较
方法 最适合 内存使用 灵活性
直接字节数组(两个文件) 简单的两文档合并 基础
List overload 批量合并多个文件
MemoryStream构造函数 基于流的集成
数据库获取模式 生产文档工作流 非常高

如何在生产中开始PDF合并?

IronPDF提供功能完整的免费试用,因此您可以在实际应用中测试PDF合并,然后再决定购买许可证。 试用版包括完整的API--合并、拆分、转换、注释、签名等--在评估期间没有功能限制。

对于生产用途,许可选项从单开发者许可证到涵盖无限部署的企业网站许可证不等。 运行高容量工作流的组织可以探索OEM许可用于可再发行的场景。

除了合并,IronPDF还涵盖了完整的PDF处理生命周期:HTML到PDF转换PDF编辑表单创建和填写文本提取数字签名安全管理。 一旦您的合并工作流正常工作,这些功能可以无任何额外依赖地嵌入。

访问IronPDF教程页面以探索每个主要功能的完整演练,或查看API参考以获取有关每个类和方法的详细文档。

NuGet 使用 NuGet 安装

PM >  Install-Package IronPdf

IronPDF 上查看 NuGet 快速安装。超过 1000 万次下载,它正以 C# 改变 PDF 开发。 您也可以下载 DLLWindows 安装程序

常见问题解答

如何使用 C# 合并两个 PDF 字节数组?

利用 IronPDF,您可以在 C# 中合并两个 PDF 字节数组。利用该库,您可以通过简单的代码示例轻松合并以字节数组、内存流甚至数据库形式存储的多个 PDF 文件。

使用 IronPDF 合并 PDF 字节数组的优势是什么?

IronPDF 通过提供直观的功能来处理 PDF 操作的复杂性,从而简化了合并 PDF 字节数组的过程,确保高效可靠的结果。

IronPDF 能否处理来自不同数据源的 PDF 合并?

是的,IronPDF 可以合并来自各种数据源(包括字节数组、内存流和数据库)的 PDF,使其成为操作 PDF 文件的多功能工具。

是否可以用 IronPDF 合并存储在内存流中的 PDF?

当然,IronPDF 支持合并存储在内存流中的 PDF,可直接在您的 C# 应用程序中实现无缝集成和合并功能。

IronPDF 是否需要其他软件来合并 PDF 字节数组?

不,IronPDF 是一个独立的库,不需要额外的软件来合并 PDF 字节数组。它可以轻松集成到您的 C# 项目中。

IronPDF 如何确保合并 PDF 的质量?

IronPDF 在合并过程中保持 PDF 的原始质量和格式,确保最终文档的高质量并保留所有原始内容。

IronPDF 合并 PDF 字节数组后可输出哪些文件格式?

合并后,IronPDF 可以以标准 PDF 格式输出最终文档,确保与任何 PDF 查看器或编辑器兼容。

IronPDF 能否合并加密的 PDF 字节数组?

是的,IronPDF 可以处理加密的 PDF 字节数组,前提是您拥有必要的权限,并在合并过程中传递正确的解密凭据。

使用 IronPDF 合并 PDF 字节数组需要哪些编码知识?

具备基本的 C# 知识就足以使用 IronPDF 合并 PDF 字节数组,因为该库提供了直接的方法和全面的文档来指导您完成整个过程。

IronPDF 的故障排除是否有任何支持?

是的,IronPDF 提供全面的文档和支持,以帮助排除在使用该库执行 PDF 操作任务时可能出现的任何问题。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我