我花了几天时间搜索、调整、部署、记录......没有任何结果。我希望这只是我的一个愚蠢的疏忽,没有被埋在这个项目中的人可以告诉我。
我尝试使用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));
}
2条答案
按热度按时间a11xaf1n1#
我仍然对Axios的工作感兴趣,但是把它换成获取,现在一切都开始运行了。
mzsu5hc02#
我已经很晚了,但是如果有人对Axios感兴趣,只需在请求的正文中添加
{responseType: "blob"}
。示例:
将正确生成blob。