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

Puppeteer生成PDF后如何通过API返回文件触发用户下载

Puppeteer生成PDF后触发前端自动下载的实现方法

核心调整点

  • 原有代码调用page.pdf()时传入了path参数,Puppeteer会直接将PDF写入服务器磁盘,不会返回文件二进制内容。如果不需要在服务器留存文件,直接去掉path参数即可拿到PDF的内存Buffer,省掉磁盘读写开销;如果需要留存服务器副本,保留path配置,生成后读取本地文件即可。
  • 必须设置正确的HTTP响应头,告知浏览器当前返回的是可下载的PDF文件,不能再返回JSON格式的响应。
  • 补充异常捕获和浏览器实例回收逻辑,避免出错时出现内存泄漏。

修改后完整代码

// 如果需要在服务器留存PDF文件,再引入fs模块,否则可以省略
const fs = require('fs/promises');

async function createPdf(req, res) {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://localhost:3000', {
      timeout: 10000,
      waitUntil: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'],
    });

    // ---------- 两种方案二选一即可 ----------
    // 方案1:不在服务器存PDF,直接返回内存中的文件,性能更优
    const pdfContent = await page.pdf({
      landscape: false,
      format: 'A4',
      margin: {
        top: '0px',
        right: '0px',
        bottom: '0px',
        left: '0px',
      },
    });

    // 方案2:需要在服务器留存PDF时使用
    // 注意不要单独用Math.random()生成文件名,重名概率高,推荐用时间戳/UUID组合
    // const pdfSavePath = `./invoices/${Date.now()}_${Math.random().toString(36).slice(2)}.pdf`;
    // await page.pdf({
    //   path: pdfSavePath,
    //   landscape: false,
    //   format: 'A4',
    //   margin: { top: '0px', right: '0px', bottom: '0px', left: '0px' },
    // });
    // const pdfContent = await fs.readFile(pdfSavePath);
    // --------------------------------

    // 设置响应头,触发浏览器下载
    res.setHeader('Content-Type', 'application/pdf');
    // filename字段为用户下载时默认显示的文件名,可按需修改
    res.setHeader('Content-Disposition', 'attachment; filename="invoice.pdf"');
    res.status(200).send(pdfContent);

  } catch (error) {
    res.status(500).send({
      success: false,
      message: 'PDF生成失败',
      errMsg: error.message
    });
  } finally {
    // 无论生成成功还是失败,都关闭浏览器实例,避免内存泄漏
    if (browser) await browser.close();
  }
}

补充说明

  • 如果是通过<a>标签直接跳转、window.open打开该接口地址,上述配置完成后即可直接触发浏览器下载,不需要额外写前端代码。
  • 如果是通过fetch/axios等AJAX方式调用接口,前端需要将响应转换为Blob对象,生成临时URL后手动触发下载,否则不会弹出下载框。
  • 采用本地存文件方案时,需要提前创建好./invoices目录,否则Puppeteer写入文件时会抛出路径不存在的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.08 16:15:17