Javascript Puppeter等待page和setTimeout在Firebase云函数中不工作
我正在尝试加载我在firebase上托管的页面,并使用Puppeter将其转换为pdf。Javascript Puppeter等待page和setTimeout在Firebase云函数中不工作,javascript,typescript,firebase,google-cloud-functions,puppeteer,Javascript,Typescript,Firebase,Google Cloud Functions,Puppeteer,我正在尝试加载我在firebase上托管的页面,并使用Puppeter将其转换为pdf。 只需一个html页面即可使用。 现在我从firebase获取数据并将其显示在我的页面中,因此我需要等待页面完全加载,然后才能创建pdf。 当我使用firebase emulator对其进行本地测试时,它工作得很好npm run serve 但是,它在云函数中不起作用,settimout只会一直等待,直到函数为timedout。 日志显示waitFor,一段时间后函数执行耗时120002毫秒,状态为:“超时”
只需一个html页面即可使用。
现在我从firebase获取数据并将其显示在我的页面中,因此我需要等待页面完全加载,然后才能创建pdf。
当我使用firebase emulator对其进行本地测试时,它工作得很好
npm run serve
但是,它在云函数中不起作用,settimout只会一直等待,直到函数为timedout。
日志显示
waitFor
,一段时间后函数执行耗时120002毫秒,状态为:“超时”
我尝试了很多我不知道该怎么做的事情,我开始认为这是云函数中的一个bug
import * as functions from 'firebase-functions';
// tslint:disable-next-line:no-duplicate-imports
import { VALID_MEMORY_OPTIONS } from 'firebase-functions';
// import * as puppeteer from 'puppeteer';
const runtimeOpts = {
timeoutSeconds: 120,
memory: VALID_MEMORY_OPTIONS[4],
};
// const cors = require('cors')({ origin: true });
export const generatePDF = functions
.runWith(runtimeOpts)
// .region('europe-west1')
.https.onRequest(async (request: any, response: any) => {
// cors(request, response, async () => {
console.log(7);
const hostname = request.hostname;
let url = '';
if (hostname === 'localhost') {
url = 'http://localhost:5000';
} else {
url = 'https://myapp.firebaseapp.com';
}
const puppeteer = require('puppeteer');
console.log('launch puppeteer');
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
// debug: {headless: false}
console.log('new page');
const page = await browser.newPage();
console.log('goto');
await page
.goto(`${url}/report/A3p71Fl5GD98Sjks5BJg`)
.catch((error: any) => {
console.log(error);
return response.send('Timeout1');
});
console.log('waitFor');
// await page.waitFor(10000).catch((error: any) => {
// console.log(error);
// return response.send('Timeout2');
// });
await new Promise(resolve => setTimeout(resolve, 5000));
// await page.waitForNavigation({
// waitUntil: 'networkidle0',
// });
// Wait for element to render
// await page.waitForSelector('#end');
// await page.waitFor(10000);
console.log('create pdf');
const pdf = await page.pdf({
format: 'A4',
});
console.log('close browser');
await browser.close();
// response.setHeader('Content-Disposition', 'attachment; filename=customfilename.pdf');
response.setHeader('Content-Disposition', 'filename=customfilename.pdf');
return response.type('application/pdf').send(pdf);
// });
});
export const helloWorld = functions.https.onRequest(
async (request: any, response: any) => {
await new Promise(resolve => setTimeout(resolve, 5000));
response.send('Hello from Firebase!');
},
);
// function delay(ms: number) {
// return new Promise(resolve => setTimeout(resolve, ms));
// }
package.json:
{
"name": "functions",
"scripts": {
"lint": "tslint --project tsconfig.json",
"build": "tsc",
"serve": "npm run build && firebase serve",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "8"
},
"main": "lib/index.js",
"dependencies": {
"cors": "^2.8.5",
"firebase-admin": "^8.7.0",
"firebase-functions": "^3.3.0",
"puppeteer": "^2.0.0"
},
"devDependencies": {
"tslint": "^5.12.0",
"typescript": "^3.6.3"
},
"private": true
}
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017",
"lib": ["dom"]
},
"compileOnSave": true,
"include": ["src"]
}
您应该在函数之外导入Puppeter,以便firebase可以重用它。您也不需要创建超时,但可以使用
wait page.waitFor(5000)代码>取而代之。我试图为您的用例创建一个最小的示例。如果您仍然遇到错误,请转到firebase functions控制台并检查日志,它们会告诉您出了什么问题
还有一种可能是您尚未启用firebase帐户的计费功能-在这种情况下,您的功能无法到达第三方主机
import * as functions from 'firebase-functions';
import * as puppeteer from 'puppeteer';
export const generatePDF = functions
.runWith({ timeoutSeconds: 30, memory: "1GB" })
.https.onRequest(async (request, response) => {
const url = 'https://www.yoururl.com/';
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
const page = await browser.newPage();
await page.goto(url);
await page.waitFor(5000);
const pdf = await page.pdf({
format: 'A4',
});
await browser.close();
response.setHeader('Content-Disposition', 'filename=customfilename.pdf');
return response.type('application/pdf').send(pdf);
});
与您的问题无关,但为什么要这样定义回调函数:(请求:any,响应:any)
?您不必向下转换这些参数来键入any
。TypeScript应该根据firebase函数提供的类型绑定自动获取它们的值。我最近经常看到有人这样做,我不知道为什么。因为tslint对此抱怨,这很烦人,我无条件地使用tslint,我从未见过它抱怨。除了Firebase CLI为您所做的之外,您是否对配置进行了其他操作?我非常好奇。这不应该是一个问题。你是对的,我删除了它,tslint没有抱怨,我使用vscode和prettier。我只看到那条黄线一次,并补充说:任何,现在不要看到它,奇怪。好的。顺便说一句,我从来没有遇到过使用setTimeout实现云中等待功能的问题。谢谢,我必须为此安装Puppeter类型?上周2.0.0版没有提供,但我会尝试一个旧版本。你不必,但它非常方便!它工作了吗?如果我查看firebase函数控制台日志,没有什么,只是一个超时。在这种情况下,我高度怀疑您超时的原因是您选择的URL。我复制粘贴了上面的确切代码(将URL调整到此处的页面),并创建了一个云函数。你可以在这里看到结果(加载需要几秒钟,但是你会得到PDF):但是如果我在本地模拟器中使用相同的url,它确实可以工作,在这里也可以工作:没有问题,有时甚至我的云功能也可以工作