如何使用 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 for 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 前,先檢查該檔案是否可讀。 若想了解社群開發者如何處理類似的合併錯誤情境,Stack Overflow 上關於在 Node.js 中合併 PDF 的討論串可提供更多背景資訊。


在 Node.js 中合併 PDF 的下一步該怎麼做?

上述範例涵蓋了最常見的合併情境:基本檔案合併、頁面範圍選取、頁首與頁尾插入、密碼保護,以及數位簽章。 IronPDF 亦支援跨合併頁面的 PDF 表單管理將新內容疊印至合併文件,以及從最終輸出產生點陣圖以供預覽。

立即開始 30 天試用,體驗無浮水印的合併功能;若您已準備好部署,亦可查看授權方案

準備好探索 IronPDF 的更多功能了嗎? 請參閱 IronPDF for Node.js 文件,以獲取完整的 API 操作指南及更多實用教學。

常見問題

如何在 Node.js 中將多個 PDF 檔案合併為一個?

使用 PdfDocument.fromFile() 載入每個檔案,將結果收集至陣列中,將陣列傳遞給 PdfDocument.merge(),並透過 toFile() 儲存輸出結果。當合併三個或更多文件時,請使用 Promise.all() 並行載入檔案。

IronPDF 在合併 PDF 時會保留格式嗎?

是的。IronPDF 會保留每個原始文件中的字型、圖片、嵌入式表單及頁面幾何結構。合併後的輸出文件會維持每頁的原始版面配置,並依照原始檔案在輸入陣列中的順序排列。

如何僅合併每個 PDF 中的特定頁面?

在將每個載入的文件傳遞給 PdfDocument.merge() 之前,請先呼叫 extractPages(startPage, endPage)。頁碼採用零起始制,因此第一頁的索引為 0。回傳的文件僅包含指定的範圍。

我可以在合併後的 PDF 中添加頁首和頁尾嗎?

是的。合併後,請在生成的文件上呼叫 addHtmlHeader()addHtmlFooter() 方法。這兩種方法皆接受 HTML 字串及選項物件作為參數。請使用 {page}{total-pages} 佔位符來自動編排頁碼。

如何在 Node.js 中為合併後的 PDF 設定密碼保護?

請透過傳入指定 userPasswordownerPassword 以及 allowUserPrinting 等權限標誌的安全選項物件,來呼叫 saveAs() 方法。userPassword 是開啟文件所需的密碼;ownerPassword 則用於控制權限設定。

若合併操作因「檔案未找到」錯誤而失敗,我該怎麼辦?

請確認所有輸入檔案路徑均為相對於 Node.js 執行程序工作目錄的相對路徑,或改用絕對路徑。在呼叫 PdfDocument.fromFile() 之前,請使用 fs.promises.access() 確認每個檔案皆可讀取。

如果其中一個原始 PDF 檔案經過加密,該怎麼辦?

若未提供密碼即嘗試載入加密的 PDF 檔案,將會引發錯誤並導致整個 Promise.all() 呼叫失敗。請在合併前,將文件密碼作為 fromFile() 的第二個參數傳入。

使用 IronPDF 合併 PDF 檔案是否需要授權金鑰?

IronPDF 在開發階段無需授權金鑰即可運作,但產出的文件會包含試用版浮水印。請在部署至生產環境前設定有效的授權金鑰,以移除浮水印並啟用完整功能。

Curtis Chau
技術撰稿人

Curtis Chau 擁有卡爾頓大學(Carleton University)的電腦科學學士學位,專精於前端開發,並精通 Node.js、TypeScript、JavaScript 及 React。他熱衷於打造直觀且美觀的用戶介面,喜歡運用現代框架,並創建結構完善、視覺上吸引人的手冊。

除了開發工作之外,Curtis 對物聯網(IoT)抱有濃厚興趣,致力於探索整合硬體與軟體的創新方法。閒暇時,他喜歡玩遊戲和開發 Discord 機器人,將對科技的熱愛與創意相結合。

準備開始了嗎?
版本: 2026.5 just released
Still Scrolling Icon

還在捲動嗎?

想要快速證明?
執行範例 觀看您的 HTML 變成 PDF。