使用 Puppeteer 在客户端生成 PDF

Use Puppeteer to Generate a PDF on Client Side

我正在努力让 Puppeteer 在浏览器中下载文件。我目前将文件保存在 Cloud9 本地,但我想单击下载 PDF 按钮并在 Chrome.

中下载它

我试过将 page.pdf 保存到一个变量,但没有成功。我也试过打开本地保存的文件,但我也无法让它工作。

var express = require("express");
var puppeteer = require("puppeteer");

module.exports = function pdf(url, req) {(async () => {
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
      await page.goto('https://node-and-mysql-mbegg.c9users.io/clients/'+req.params.id+'/reports/monthlyreport/'+req.params.marketplace+'/'+req.params.month, {waitUntil: 'networkidle2'});
      await page.pdf({path: 'public/temp/Performance Report -'+req.params.month+'.pdf', format: 'A4', landscape: true, printBackground: true});

      await browser.close();
    })();
}

///Print PDF route
router.get("/clients/:id/reports/monthlyreport/:marketplace/:month/pdf", function(req, res) {
    var url = "/clients/"+req.params.id+"/reports/monthlyreport/"+req.params.marketplace+"/"+req.params.month
    pdf(url, req);
    res.contentType("application/pdf");
    res.send(pdf);;
});

我知道 res.send(pdf) 需要将 page.pdf() 保存到变量并返回。我只是没有从路线中删除它。

当我将 page.pdf() 保存到一个变量时会发生什么,我只是得到一个空白文件 - 而不是 PDF - 它看起来只是一个空白缓冲区或其他东西。我不知道我做错了什么,无法在 chrome 浏览器中下载文件。

///  Async/await
router.get("/clients/:id/reports/monthlyreport/:marketplace/:month/pdf", async function(req, res) {
    var url = "/clients/"+req.params.id+"/reports/monthlyreport/"+req.params.marketplace+"/"+req.params.month
    let pdfData = await pdf(url, req);
    res.contentType("application/pdf");
    res.send(pdfData);
});
///  promise
router.get("/clients/:id/reports/monthlyreport/:marketplace/:month/pdf", async function(req, res) {
    var url = "/clients/"+req.params.id+"/reports/monthlyreport/"+req.params.marketplace+"/"+req.params.month
    pdf(url, req).then((pdfData )=>{
        res.contentType("application/pdf");
        res.send(pdfData);
    });
});

您既没有等待进程完成。您还发送了函数,而不是它的结果。

虽然在 pdf 函数中您似乎在等待每个 puppeteer 操作,但实际上您并没有在快速路由中等待对 pdf 函数的调用。此外,pdf 函数现在没有 return 任何东西!您需要对程序进行以下修改:

  1. pdf 函数应该 return 生成的 PDF 保存到的文件名。当你这样做的时候,最好去掉 IIFE,它不是必需的,只需将外部函数声明为 async.
  2. 在路由函数中,程序应该等待文件名从 pdf(...) 调用中被 returned。这可以通过使用 Promise 或 await 来完成。如果首选等待,则外部函数应标记为异步。
  3. express 在response 对象上提供的sendFile 函数应该用于将生成的PDF 文件发送给客户端。请注意 res.sendFile 需要文件的绝对路径。

以下是您的程序的修改版本,它实现了这些。

const express = require("express");
const puppeteer = require("puppeteer");
const path = require('path');

module.exports = async function pdf(url, req) {
    const filename = `public/temp/Performance Report -${req.params.month}.pdf`;
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.goto('https://node-and-mysql-mbegg.c9users.io/clients/'+req.params.id+'/reports/monthlyreport/'+req.params.marketplace+'/'+req.params.month, {waitUntil: 'networkidle2'});
    await page.pdf({path: filename, format: 'A4', landscape: true, printBackground: true});

    await browser.close();
    return filename;
}

///Print PDF route
router.get("/clients/:id/reports/monthlyreport/:marketplace/:month/pdf", async function(req, res) {
    var url = "/clients/"+req.params.id+"/reports/monthlyreport/"+req.params.marketplace+"/"+req.params.month

    const filename = await pdf(url, req);
    res.contentType("application/pdf");
    res.sendFile(path.join(__dirname, filename)); // if 'public/temp/...' path is not relative to cur dir, make relevant change here.
});