Puppeteer 等待页面和 setTimeout 在 Firebase 云功能中不起作用
Posted
技术标签:
【中文标题】Puppeteer 等待页面和 setTimeout 在 Firebase 云功能中不起作用【英文标题】:Puppeteer wait for page and setTimeout not working in Firebase cloud functions 【发布时间】:2020-03-02 06:05:25 【问题描述】:我正在尝试加载我在 firebase 上托管的页面,并使用 Puppeteer 将其转换为 pdf。
它只适用于一个 html 页面。
现在我从 firebase 获取数据并将其显示在我的页面中,因此我需要等待页面完全加载,然后才能创建 pdf。
当我使用 firebase 模拟器 npm run serve
在本地测试它时,效果很好。
但是它在云函数中不起作用,settimout 只是一直等到函数超时。
日志显示waitFor
,一段时间后显示Function execution took 120002 ms, finished with status: 'timeout'
。
我尝试了很多我不知道该怎么做的事情,我开始认为这是云功能中的错误。
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"]
【问题讨论】:
与您的问题无关,但您为什么要这样定义回调函数:(request: any, response: any)
?您不必将这些参数向下转换为类型any
。 TypeScript 应该根据 firebase-functions 提供的类型绑定自动获取它们的值。我最近经常看到人们这样做,我不知道为什么。
因为 tslint 抱怨它,很烦人
我无条件使用 tslint,我从未见过它抱怨。您是否在 Firebase CLI 为您做的事情之外对配置做一些事情?我超级好奇。这根本不应该是一个问题。
你是对的,我删除了它并且 tslint 没有抱怨,我使用 vscode 和更漂亮。我只看到那条黄线一次并补充说:任何,但现在看不到它,很奇怪。
好的。顺便说一句,我从来没有遇到过使用 setTimeout 在 Cloud Functions 中实现等待的问题。
【参考方案1】:
您应该在您的函数之外导入 puppeteer,以便 firebase 可以重用它。此外,您不需要创建超时,但您可以使用 await page.waitFor(5000);
代替。我试图为您的用例创建一个最小的示例。如果您仍然遇到错误,请转到您的 firebase 函数控制台并检查日志,它们应该会告诉您出了什么问题。
还有一种可能是您没有在您的 firebase 帐户上启用结算功能 - 在这种情况下,您的函数无法访问 3rd 方主机。
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);
);
【讨论】:
谢谢,我必须为此安装 puppeteer 类型?上周 2.0.0 版本不可用,但我会尝试旧版本 您不必这样做,但是非常方便!成功了吗? 如果我去firebase函数控制台日志,什么都没有,只是超时 在这种情况下,我高度怀疑您超时的原因是您选择的 URL。我复制粘贴了上面的确切代码(在此处调整此页面的 URL)并创建了一个云功能。您可以在此处查看结果(加载需要几秒钟,但随后您将获得 PDF):us-central1-ankerguru.cloudfunctions.net/generatePDF 但是如果我在本地模拟器中使用相同的 url 它确实可以工作,它也可以在这里工作:puppeteersandbox.com 没有问题,有时甚至我的云功能也可以工作以上是关于Puppeteer 等待页面和 setTimeout 在 Firebase 云功能中不起作用的主要内容,如果未能解决你的问题,请参考以下文章