You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用html-pdf-node+Promise.all批量生成PDF遇Target closed协议错误求助

问题描述

使用html-pdf-node包批量生成PDF时,通过Array.map()创建异步Promise数组并使用Promise.all()等待全部完成,但生成部分文件后出现Protocol error (Page.printToPDF): Target closed错误。

代码示例

async function generatePDFs(result) {
        const filename = [];
      
        const generatePdfPromiseArray = result.map(async (item) => {
          const file_name = getRandomFileName();
          const html = "abc";
      
          const margin = {
            top: "210px",
            right: "100px",
            bottom: "150px",
            left: "80px",
          };
      
          const options = {
            format: "A4",
            path: `some/path/${file_name}.pdf`,
            margin: margin,
          };
      
          try {
            const pdfBuffer = await html_to_pdf.generatePdf({ content: html }, options);
            await fs.writeFileSync(options.path, pdfBuffer);
            filename.push(file_name);
            console.log("PDF saved successfully!");
          } catch (error) {
            console.error("Error generating PDF:", error);
          }
        });
      
        // Wait for all the promises to resolve
        await Promise.all(generatePdfPromiseArray);
      
        return filename;
      }

解决思路与方案

这个错误一般是因为html-pdf-node依赖的Puppeteer实例被过早关闭,或是并发任务过多导致浏览器资源耗尽,可从以下方向修复:

1. 限制并发数,避免资源过载

Promise.all()会一次性触发所有异步任务,瞬间创建大量Puppeteer页面/浏览器实例,超出资源承载上限。可以改用串行或限制并发的方式:

串行处理(适合小批量场景)

async function generatePDFs(result) {
  const filename = [];
  // 逐个处理任务,避免并发过载
  for (const item of result) {
    const file_name = getRandomFileName();
    const html = "abc";
    const margin = {
      top: "210px",
      right: "100px",
      bottom: "150px",
      left: "80px",
    };
    const options = {
      format: "A4",
      path: `some/path/${file_name}.pdf`,
      margin: margin,
    };
    try {
      await html_to_pdf.generatePdf({ content: html }, options);
      filename.push(file_name);
      console.log("PDF saved successfully!");
    } catch (error) {
      console.error("Error generating PDF:", error);
    }
  }
  return filename;
}

限制并发数(适合大批量场景)

借助p-limit包控制同时运行的任务数量:

const pLimit = require('p-limit');
const limit = pLimit(3); // 限制同时运行3个PDF生成任务

async function generatePDFs(result) {
  const filename = [];
  const generatePdfPromiseArray = result.map(item => limit(async () => {
    const file_name = getRandomFileName();
    const html = "abc";
    const margin = {
      top: "210px",
      right: "100px",
      bottom: "150px",
      left: "80px",
    };
    const options = {
      format: "A4",
      path: `some/path/${file_name}.pdf`,
      margin: margin,
    };
    try {
      await html_to_pdf.generatePdf({ content: html }, options);
      filename.push(file_name);
      console.log("PDF saved successfully!");
    } catch (error) {
      console.error("Error generating PDF:", error);
    }
  }));

  await Promise.all(generatePdfPromiseArray);
  return filename;
}

2. 复用Puppeteer浏览器实例

html-pdf-node默认每次调用generatePdf都会创建新浏览器实例,批量处理时会浪费资源。可以手动创建一个浏览器实例复用:

const puppeteer = require('puppeteer');

async function generatePDFs(result) {
  const filename = [];
  // 提前创建浏览器实例
  const browser = await puppeteer.launch();
  
  const generatePdfPromiseArray = result.map(async (item) => {
    const file_name = getRandomFileName();
    const html = "abc";
    const margin = {
      top: "210px",
      right: "100px",
      bottom: "150px",
      left: "80px",
    };
    const options = {
      format: "A4",
      path: `some/path/${file_name}.pdf`,
      margin: margin,
      browser: browser, // 复用已创建的浏览器实例
    };
    try {
      await html_to_pdf.generatePdf({ content: html }, options);
      filename.push(file_name);
      console.log("PDF saved successfully!");
    } catch (error) {
      console.error("Error generating PDF:", error);
    }
  });

  await Promise.all(generatePdfPromiseArray);
  await browser.close(); // 所有任务完成后关闭浏览器
  return filename;
}

3. 移除冗余IO操作

原代码中options已指定path,html_to_pdf.generatePdf会自动将PDF写入该路径,无需再调用fs.writeFileSync重复写入,这会额外占用IO资源,直接移除即可:

// 移除这行冗余代码
// await fs.writeFileSync(options.path, pdfBuffer);

内容的提问来源于stack exchange,提问作者Rizwan Raza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:55:35