NodeJS Puppeteer PDF从云端生成空白页面,与 Postman 一起工作,有什么想法吗?

j2datikz  于 2022-11-29  发布在  Node.js
关注(0)|答案(2)|浏览(286)

我花了几天时间搜索、调整、部署、记录......没有任何结果。我希望这只是我的一个愚蠢的疏忽,没有被埋在这个项目中的人可以告诉我。
我尝试使用Puppeteer从Firebase Cloud Functions托管的Node.js Express API端点创建PDF,作为前端Vue应用程序的微服务。我使用Axios发出发布请求,并在正文中传递HTML字符串。使用bodyparser,我能够将数据分配给变量。

const express = require('express');
const functions = require('firebase-functions');
const puppeteer = require('puppeteer');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();

const corsConfig = {
    origin: true,
    credentials: true,
}

app.use(cors(corsConfig));
app.options('*', cors(corsConfig));

app.all('*', async (request, response, next) => {
    response.locals.browser = await puppeteer.launch({
        dumpio: true,
        headless: true,
        args: ['--no-sandbox', '--disable-setuid-sandbox']
    });
    next();
});

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.post('/pdf', async (request, response) => {
    const html = request.body.html;

    //console.log(typeof html, html);

    if (!html) {
        return response.status(400).send(
            'Something happened so I cannot create your PDF right now.');
    }

    const browser = response.locals.browser;
    const page = await browser.newPage();

    //await page.goto(`data:text/html,${encodeURIComponent(html)}`, { waitUntil: 'networkidle0' });

    await page.setContent(html, { waitUntil: ['domcontentloaded', 'load', "networkidle0"] }, { timeout: 0 });
    await page.addStyleTag({ path: 'style.css' });

    const pdf = await page.pdf({
        format: 'Letter',
        printBackground: true,
    });

    //console.log(pdf);
    response.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length });
    response.send(pdf);

    await browser.close();

    return pdf;
});

const opts = { memory: '2GB', timeoutSeconds: 60 };
exports.pdf = functions.runWith(opts).https.onRequest(app);

控制台日志显示变量是一个字符串,标记看起来很好。当我使用它到page.setContent(html, {waitUntil: 'networkidle0'}),然后page.pdf(),我得到了一个有3个空白页的PDF。Screenshot of Functions Log
我已经尝试了整个page.goto的东西和相同的结果。如果我改变了HTML字符串的东西短得多,如'<h1>Hello World</h1>'(尝试了变量和实际字符串),我得到了一个只有一个空白页的PDF。这让我相信它理解我,只是没有呈现。我将所有的id都改为class,并将十六进制颜色转换为RGB以避免#冲突(我读过它们会把事情搞砸)。
我试过使用一个车把模板,只在请求正文中发送JSON数据,也没有成功。现在,如果我使用postman,在正文中发送包含HTML字符串的请求,我会得到我想要的结果。缺点是它没有AXIOS代码段供我使用,也没有在我的应用中复制结果。
这是前端

savePDF() {
      this.getPDF()
        .then(response => {
          const blob = new Blob([response.data], { type: "application/pdf" });
          const link = document.createElement("a");
          link.href = window.URL.createObjectURL(blob);
          link.target = "_blank";
          link.click();
        })
        .catch(err => alert(err));
    },

    async getPDF() {
      const html = document.querySelector(".wrapper").outerHTML;

      axios.defaults.withCredentials = true;

      const pdf = await axios
        .post("MYENDPOINTURL/pdf", { html })
        .then(
          response => {
            return response;
          },
          error => {
            alert(error);
          }
        );
      return pdf;
    }

UPDATE我放弃了Axios,使用了一个fetch post请求......它成功了!通过这个,我改变了getPDF和savePDF前端函数(服务器端保持不变):

async savePDF() {
      const html = document.querySelector(".wrapper").outerHTML;
      var myHeaders = new Headers();
      myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
      var urlencoded = new URLSearchParams();
      urlencoded.append("html", html);
      var requestOptions = {
        method: "POST",
        headers: myHeaders,
        body: urlencoded,
        redirect: "follow"
      };

      fetch("MYENDPOINTURL/pdf", requestOptions)
        .then(response => response.blob())
        .then(result => {
          const blob = new Blob([result], { type: "application/pdf" });
          const link = document.createElement("a");
          link.href = window.URL.createObjectURL(blob);
          link.target = "_blank";
          //link.download = `${this.propertyInfo.property}-${this.propertyInfo.workType}.pdf`;
          link.click();
        })
        .catch(error => alert("error", error));
    }
a11xaf1n

a11xaf1n1#

我仍然对Axios的工作感兴趣,但是把它换成获取,现在一切都开始运行了。

const html = document.querySelector(".wrapper").outerHTML;
      var myHeaders = new Headers();
      myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
      var urlencoded = new URLSearchParams();
      urlencoded.append("html", html);
      var requestOptions = {
        method: "POST",
        headers: myHeaders,
        body: urlencoded,
        redirect: "follow"
      };

      fetch("MYENDPOINTURL/pdf", requestOptions)
        .then(response => response.blob())
        .then(result => {
          const blob = new Blob([result], { type: "application/pdf" });
          const link = document.createElement("a");
          link.href = window.URL.createObjectURL(blob);
          link.target = "_blank";
          //link.download = `${this.propertyInfo.property}-${this.propertyInfo.workType}.pdf`;
          link.click();
        })
        .catch(error => alert("error", error));
mzsu5hc0

mzsu5hc02#

我已经很晚了,但是如果有人对Axios感兴趣,只需在请求的正文中添加{responseType: "blob"}
示例:

const res = await api.get(`/my-pdf`, { responseType: "blob" });
    return res.data

将正确生成blob。

相关问题