如何使用 Node.js 将 PDF 文件合并为一个 PDF 文件

This article was translated from English: Does it need improvement?
Translated
View the article in English

IronPDF让您在Node.js中仅需几行代码即可将多个PDF文件合并为一个文档。 使用 PdfDocument.fromFile() 加载每个文件,将数组传递给 PdfDocument.merge(),并使用 toFile() 保存结果。

快速入门:合并PDF文件

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/quickstart-merge.js
// Install: npm install ironpdf
const IronPdf = require('ironpdf');

async function quickMerge() {
  const docs = await Promise.all([
    IronPdf.PdfDocument.fromFile('doc1.pdf'),
    IronPdf.PdfDocument.fromFile('doc2.pdf'),
    IronPdf.PdfDocument.fromFile('doc3.pdf'),
  ]);
  const merged = await IronPdf.PdfDocument.merge(docs);
  await merged.toFile('merged-output.pdf');
}

quickMerge();
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/quickstart-merge.js
// Install: npm install ironpdf
const IronPdf = require('ironpdf');

async function quickMerge() {
  const docs = await Promise.all([
    IronPdf.PdfDocument.fromFile('doc1.pdf'),
    IronPdf.PdfDocument.fromFile('doc2.pdf'),
    IronPdf.PdfDocument.fromFile('doc3.pdf'),
  ]);
  const merged = await IronPdf.PdfDocument.merge(docs);
  await merged.toFile('merged-output.pdf');
}

quickMerge();
JAVASCRIPT

IronPDF是Node.js的PDF操作库,处理HTML到PDF转换、文档组装和PDF修改,而无需任何系统级的PDF工具。 在合并文档时,它保留每个源文件中的字体、图像、嵌入式表单和页面几何。

在部署到生产环境之前,请配置您的IronPDF许可证密钥以去除试用水印并启用完整输出。 要为您的操作系统设置渲染引擎,请遵循IronPdfEngine配置指南


在Node.js中如何合并多个PDF文件?

PdfDocument.merge() 接受一个 PdfDocument 对象数组,并返回一个新文档,其中包含源文档数组中所有页面的顺序。 该操作是非破坏性的;原始文件对象不会被修改。

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-pdfs.js
const IronPdf = require('ironpdf');

async function mergePdfs(outputFilePath, inputFiles) {
  // Load all source documents in parallel
  const pdfDocs = await Promise.all(
    inputFiles.map(file => IronPdf.PdfDocument.fromFile(file))
  );

  // Combine into one document, preserving page order
  const mergedPdf = await IronPdf.PdfDocument.merge(pdfDocs);

  // Write the result to disk
  await mergedPdf.toFile(outputFilePath);
  console.log(`Merged PDF saved to ${outputFilePath}`);
}

(async () => {
  const inputFiles = ['report-jan.pdf', 'report-feb.pdf', 'report-mar.pdf'];
  await mergePdfs('quarterly-report.pdf', inputFiles);
})();
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-pdfs.js
const IronPdf = require('ironpdf');

async function mergePdfs(outputFilePath, inputFiles) {
  // Load all source documents in parallel
  const pdfDocs = await Promise.all(
    inputFiles.map(file => IronPdf.PdfDocument.fromFile(file))
  );

  // Combine into one document, preserving page order
  const mergedPdf = await IronPdf.PdfDocument.merge(pdfDocs);

  // Write the result to disk
  await mergedPdf.toFile(outputFilePath);
  console.log(`Merged PDF saved to ${outputFilePath}`);
}

(async () => {
  const inputFiles = ['report-jan.pdf', 'report-feb.pdf', 'report-mar.pdf'];
  await mergePdfs('quarterly-report.pdf', inputFiles);
})();
JAVASCRIPT

Promise.all() 支持并行加载源文件而非逐个加载,这在处理大型数据集时尤为重要。 merge() 调用会按数组顺序拼接文档,因此请按期望的输出顺序将文件放入数组中。

提示传递文件路径为绝对路径或相对Node.js进程工作目录的路径。 fromFile() 方法无法解析相对于脚本文件本身的路径。

代码的每个部分是做什么的?

  • PdfDocument.fromFile():从磁盘读取 PDF 文件,并返回一个 PdfDocument 对象。 完整方法签名请参阅IronPDF for Node.js API参考
  • Promise.all():同时分发所有文件加载操作,从而缩短多文档合并的总加载时间。 此模式也适合多线程和并发PDF生成场景。
  • PdfDocument.merge():将已加载文档的数组合并为单个 PdfDocument,同时保留每个源文档中的所有格式、图片和嵌入内容。
  • toFile():将合并后的文档写入指定路径。 将其与PDF压缩结合使用,以在需要时减少输出文件大小。

如何合并每个PDF的特定页面?

传递每个源文档的每一页并不总是目的。 若需合并各输入文件中的特定页面范围,请在将文档传递给 merge() 之前,先提取所需页面。

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-specific-pages.js
const IronPdf = require('ironpdf');

// Each entry specifies a file and the range of pages to include (zero-indexed)
const pageRanges = [
  { file: 'contract.pdf',  startPage: 0, endPage: 2 },
  { file: 'appendix.pdf',  startPage: 0, endPage: 0 },
  { file: 'signature.pdf', startPage: 0, endPage: 0 },
];

async function mergeSpecificPages(outputFile, ranges) {
  const pdfsToMerge = [];

  for (const range of ranges) {
    const pdf = await IronPdf.PdfDocument.fromFile(range.file);
    // extractPages returns a new PdfDocument with only the specified page range
    const pages = pdf.extractPages(range.startPage, range.endPage);
    pdfsToMerge.push(pages);
  }

  const merged = await IronPdf.PdfDocument.merge(pdfsToMerge);
  await merged.toFile(outputFile);
}

mergeSpecificPages('assembled-contract.pdf', pageRanges);
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-specific-pages.js
const IronPdf = require('ironpdf');

// Each entry specifies a file and the range of pages to include (zero-indexed)
const pageRanges = [
  { file: 'contract.pdf',  startPage: 0, endPage: 2 },
  { file: 'appendix.pdf',  startPage: 0, endPage: 0 },
  { file: 'signature.pdf', startPage: 0, endPage: 0 },
];

async function mergeSpecificPages(outputFile, ranges) {
  const pdfsToMerge = [];

  for (const range of ranges) {
    const pdf = await IronPdf.PdfDocument.fromFile(range.file);
    // extractPages returns a new PdfDocument with only the specified page range
    const pages = pdf.extractPages(range.startPage, range.endPage);
    pdfsToMerge.push(pages);
  }

  const merged = await IronPdf.PdfDocument.merge(pdfsToMerge);
  await merged.toFile(outputFile);
}

mergeSpecificPages('assembled-contract.pdf', pageRanges);
JAVASCRIPT

extractPages(startPage, endPage) 接受从零开始的页码索引。 传递 0, 0 仅提取第一页,而 0, 2 则提取前三页。 该循环会按 ranges 中出现的顺序构建一个页面范围文档数组,然后 merge() 将其拼接成最终输出。

该模式在从签名页、附录和保存在单独文件中的封面组装合同时很有用。 您可以从每个源中准确收集重要的页面,而不会在磁盘上重复文件。

请注意IronPDF中的页面索引是从零开始的。 文档的第1页是索引0,第2页是索引1,依此类推。)]}


如何向合并后的PDF添加页眉和页脚?

合并后,在生成的文档中调用 addHtmlHeader()addHtmlFooter(),以在所有页面上应用统一的页眉和页脚。 这些方法接受HTML字符串和一个选项对象。

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-headers-footers.js
const IronPdf = require('ironpdf');

async function mergeWithHeadersFooters(inputFiles, outputFile) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Apply a styled header to every page
  await mergedPdf.addHtmlHeader('<h3 style="color:#333;">Quarterly Report</h3>', {
    height: 25,
    drawDividerLine: true,
  });

  // Apply a page-numbering footer
  await mergedPdf.addHtmlFooter('<p style="font-size:10px;">Page {page} of {total-pages}</p>', {
    height: 20,
    drawDividerLine: true,
  });

  await mergedPdf.toFile(outputFile);
}

mergeWithHeadersFooters(['q1.pdf', 'q2.pdf', 'q3.pdf'], 'annual-report.pdf');
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-headers-footers.js
const IronPdf = require('ironpdf');

async function mergeWithHeadersFooters(inputFiles, outputFile) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Apply a styled header to every page
  await mergedPdf.addHtmlHeader('<h3 style="color:#333;">Quarterly Report</h3>', {
    height: 25,
    drawDividerLine: true,
  });

  // Apply a page-numbering footer
  await mergedPdf.addHtmlFooter('<p style="font-size:10px;">Page {page} of {total-pages}</p>', {
    height: 20,
    drawDividerLine: true,
  });

  await mergedPdf.toFile(outputFile);
}

mergeWithHeadersFooters(['q1.pdf', 'q2.pdf', 'q3.pdf'], 'annual-report.pdf');
JAVASCRIPT

占位符 {page}{total-pages} 将在渲染时根据合并文档的页数进行解析。 请使用 drawDividerLine: true 来在视觉上将页眉或页脚与页面内容区分开来。

在合并后应用页眉和页脚意味着合并文档中的每一页都得到相同的处理,无论它来自哪个源文件。 欲了解完整的页眉和页脚配置选项,请参阅HTML页眉和页脚示例


如何用密码保护合并后的PDF?

合并后,请通过调用 saveAs() 并传入安全选项对象来应用密码保护和权限限制。 这可以防止对合并文档的未经授权访问。

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-security.js
const IronPdf = require('ironpdf');

async function mergeWithSecurity(inputFiles, outputFile) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Restrict the merged document to print-only access
  await mergedPdf.saveAs(outputFile, {
    userPassword: 'viewerpass',
    ownerPassword: 'adminpass',
    allowUserAnnotations: false,
    allowUserCopyPasteContent: false,
    allowUserFormData: false,
    allowUserPrinting: true,
  });
}

mergeWithSecurity(['invoice-1.pdf', 'invoice-2.pdf'], 'secured-invoices.pdf');
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-security.js
const IronPdf = require('ironpdf');

async function mergeWithSecurity(inputFiles, outputFile) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Restrict the merged document to print-only access
  await mergedPdf.saveAs(outputFile, {
    userPassword: 'viewerpass',
    ownerPassword: 'adminpass',
    allowUserAnnotations: false,
    allowUserCopyPasteContent: false,
    allowUserFormData: false,
    allowUserPrinting: true,
  });
}

mergeWithSecurity(['invoice-1.pdf', 'invoice-2.pdf'], 'secured-invoices.pdf');
JAVASCRIPT

打开文档时必须包含 userPasswordownerPassword 用于控制权限设置本身。 在禁用其他权限标志的同时设置 allowUserPrinting: true,可允许收件人打印文档,但禁止编辑、复制和批注。 有关可用权限标志的完整列表,请参阅IronPDF Node.js API参考

重要如果源 PDF 文件已设置密码保护,请在加载时通过 fromFile() 提供每个文档的解密密码。 尝试合并加密文档而不提供其密码将导致错误。)]}


如何合并PDF并添加数字签名?

合并PDF并立即签署结果会生成一个覆盖所有源内容的单一签署文档。 在 merge() 之后添加 applySignature(),以便将数字证书附加到合并后的输出中。

//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-signature.js
const IronPdf = require('ironpdf');

async function mergeAndSign(inputFiles, outputFile, pfxPath, pfxPassword) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Attach a digital signature using a PFX certificate
  await mergedPdf.applySignature(pfxPath, pfxPassword);
  await mergedPdf.toFile(outputFile);
}

mergeAndSign(
  ['section-a.pdf', 'section-b.pdf'],
  'signed-report.pdf',
  'certificate.pfx',
  'certpass'
);
//:path=/static-assets/pdf/content-code-examples/how-to/nodejs-merge-pdf/merge-with-signature.js
const IronPdf = require('ironpdf');

async function mergeAndSign(inputFiles, outputFile, pfxPath, pfxPassword) {
  const docs = await Promise.all(
    inputFiles.map(f => IronPdf.PdfDocument.fromFile(f))
  );
  const mergedPdf = await IronPdf.PdfDocument.merge(docs);

  // Attach a digital signature using a PFX certificate
  await mergedPdf.applySignature(pfxPath, pfxPassword);
  await mergedPdf.toFile(outputFile);
}

mergeAndSign(
  ['section-a.pdf', 'section-b.pdf'],
  'signed-report.pdf',
  'certificate.pfx',
  'certpass'
);
JAVASCRIPT

applySignature() 方法将证书嵌入 PDF 元数据中,以便读者验证文档的完整性。 此工作流程在合同装配管道中很常见,其中多个部分在发布前组合并共同签署。 有关基于证书的签名的完整演练,请参阅数字签名示例

提示在需要一个证明整个合并文档的单一证书时,签署合并后的文档而不是单个源文件。


如何排除常见的PDF合并错误?

合并过程中大多数错误属于四类:文件缺失、内存耗尽、输入损坏和权限问题。 下表列出了最常见的原因以及如何解决它们。

常见PDF合并错误及其解决方案
错误可能原因解决方案
找不到文件路径错误或工作目录不匹配使用绝对路径或验证进程工作目录
JavaScript堆内存不足同时加载许多大PDF增加Node.js内存:node --max-old-space-size=4096 script.js
无效或损坏的PDF源文件损坏或不是有效的PDF在处理之前用PDF阅读器验证源文件
权限被拒输入没有读取访问权限或输出目录没有写入权限检查操作系统上的文件和目录权限
加密的源PDF输入PDF需要密码才能打开将密码作为第二个参数传给fromFile()

对于特定环境的设置问题,IronPDF变更日志记录已知问题和修复。 如果检查上表后问题仍然存在,请将 merge 调用包裹在 try-catch 代码块中,以显示库的完整错误信息。

注意在调用 fromFile() 之前,请务必验证输入数组中的每个文件路径是否存在。 仅缺失一个文件就会导致整个 Promise.all() 调用被拒绝,从而取消合并。)}]

Node.js本身提供了用于飞行前路径验证的有用工具。 Node.jsfs.promises.access 方法允许您在将文件传递给 IronPDF 前,先检查该文件是否可读。 对于关于社区开发人员如何处理类似合并错误场景的问题,在Node.js中合并PDF的Stack Overflow线程提供了额外的上下文。


在Node.js中合并PDF的下一步是什么?

上述示例涵盖最常见的合并场景:基本文件组合、页面范围选择、页眉和页脚注入、密码保护和数字签名。 IronPDF还支持在合并页面上管理PDF表单将新内容加盖到合并文档上,以及从最终输出中生成栅格化图像以进行预览生成。

开始您的30天免费试用以测试合并不带水印,或查看许可选项如果您准备部署。

准备看看IronPDF还能做些什么吗? 访问IronPDF for Node.js文档以获取完整的API演练和其他操作指南。

常见问题解答

如何在Node.js中将多个PDF文件合并为一个?

使用PdfDocument.fromFile()加载每个文件,将结果收集到一个数组中,将数组传递给PdfDocument.merge(),并使用toFile()保存输出。当合并三个或更多文档时,使用Promise.all()并行加载文件。

合并PDF时IronPDF是否保留格式?

是的。IronPDF保留每个源文档中的字体、图像、嵌入表单和页面几何体。合并后的输出保持输入数组中源文件出现顺序的每页的原始布局。

如何仅合并每个PDF中的特定页面?

在传入PdfDocument.merge()之前,在每个加载的文档上调用extractPages(startPage, endPage)。页面索引是从零开始的,因此第一页是索引0。返回的文档仅包含指定范围。

我可以为合并的PDF添加页眉和页脚吗?

可以。在合并后,调用结果文档上的addHtmlHeader()addHtmlFooter()。这两种方法都接受HTML字符串和一个选项对象。使用{page}{total-pages}占位符实现自动分页。

如何在Node.js中为合并的PDF设置密码保护?

使用安全选项对象调用saveAs(),指定userPasswordownerPassword和权限标志,例如allowUserPrintinguserPassword是打开文档所需的,ownerPassword控制权限设置。

如果合并失败并出现文件未找到错误怎么办?

验证所有输入文件路径是否相对于Node.js进程工作目录正确,或切换到绝对路径。使用fs.promises.access()确认每个文件在调用PdfDocument.fromFile()之前是可读的。

如果其中一个源PDF被加密怎么办?

尝试加载未提供密码的加密PDF将抛出错误并拒绝整个Promise.all()调用。在合并之前,将文档密码作为第二个参数提供给fromFile()

我需要额度密钥才能使用IronPDF合并PDF吗?

在开发期间,IronPDF不需要许可证密钥,但输出文档中会包含试用水印。在部署到生产之前配置有效许可证密钥以消除水印并启用完整功能。

Darrius Serrant
全栈软件工程师(WebOps)

Darrius Serrant 拥有迈阿密大学的计算机科学学士学位,目前在 Iron Software 担任全栈 WebOps 市场工程师。从小就被编码吸引,他认为计算机既神秘又易于接触,使其成为创意和问题解决的理想媒介。

在 Iron Software,Darrius 喜欢创造新事物,并简化复杂概念以使其更易理解。作为我们常驻的开发者之一,他还自愿教授学生,与下一代分享他的专业知识。

对于 Darrius 来说,他的工作令人满意,因为它被重视并产生真正的影响。

准备开始了吗?
版本: 2026.5 just released
Still Scrolling Icon

还在滚动吗?

想快速获得证据?
运行示例看着你的HTML代码变成PDF文件。