PDFJS结合Firebase Cloud Functions出现证书过期异常(本地正常部署报错)
我原本通过PDFJS从PDF文档提取纯文本,并使用Firebase Cloud Functions调度这个任务,一切正常。但最近有两个特定域名的PDF文件总是触发**"certificate has expired"**错误。我已经确认这两个域名的SSL证书是有效的,而且本地运行提取代码完全没问题,一部署到Cloud Functions就报错。
错误堆栈信息
Error at BaseExceptionClosure (/srv/node_modules/pdfjs-dist/build/pdf.js:666:29) at Object.<anonymous> (/srv/node_modules/pdfjs-dist/build/pdf.js:669:2) at __w_pdfjs_require__ (/srv/node_modules/pdfjs-dist/build/pdf.js:52:30) at Object.defineProperty.value (/srv/node_modules/pdfjs-dist/build/pdf.js:129:23) at __w_pdfjs_require__ (/srv/node_modules/pdfjs-dist/build/pdf.js:52:30) at pdfjsVersion (/srv/node_modules/pdfjs-dist/build/pdf.js:116:18) at /srv/node_modules/pdfjs-dist/build/pdf.js:119:10 at webpackUniversalModuleDefinition (/srv/node_modules/pdfjs-dist/build/pdf.js:25:20) at Object.<anonymous> (/srv/node_modules/pdfjs-dist/build/pdf.js:32:3) at Module._compile (module.js:653:30) at Object.Module._extensions..js (module.js:664:10) at Module.load (module.js:566:32) at tryModuleLoad (module.js:506:12) at Function.Module._load (module.js:498:3) at Module.require (module.js:597:17) at require (internal/module.js:11:18) at Object.<anonymous> (/srv/pdf/pdf.js:7:18) at Module._compile (module.js:653:30) at Object.Module._extensions..js (module.js:664:10) at Module.load (module.js:566:32) at tryModuleLoad (module.js:506:12) at Function.Module._load (module.js:498:3) message: 'certificate has expired', name: 'UnknownErrorException', details: 'UnknownErrorException: certificate has expired' }
相关代码
const pdfjslib = require('pdfjs-dist'); const functions = require('firebase-functions'); module.exports = functions.https.onRequest((req, res) => { let url = req.query.url return extractPlainTextFromPdf(url) .then(pb => { return res.send(pb) }) .catch(err => { console.log(err) return res.send("Err occured") }) }); function extractPlainTextFromPdf(pdfUrl) { let options = setupPdfOptions(pdfUrl) return getPlainBody(options) .then((plainBody) => plainBody) .catch((err) => { console.log("Err plainBody", err) //<== Error thrown here }) } async function getPlainBody(options) { return getDocument(options) .then(doc => extractTexts(doc, doc.numPages)) } function getDocument(options) { var loadingTask = pdfjslib.getDocument(options) return loadingTask.promise .then((doc) => doc) } function setupPdfOptions(url) { return { url: url, httpHeaders: { "User-Agent": "MY-USER-AGENT", }, }; }
package.json配置
{ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "serve": "firebase serve --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log", }, "engines": { "node": "8" }, "dependencies": { "@google-cloud/functions-framework": "^1.5.1", "@google-cloud/vision": "^1.11.0", "aws-sdk": "^2.667.0", "axios": "^0.19.2", "cheerio": "^1.0.0-rc.3", "diff-match-patch": "^1.0.4", "firebase-admin": "^8.11.0", "firebase-functions": "^3.6.1", "moment": "^2.25.0", "nodemailer": "^6.4.6", "pdfjs-dist": "^2.3.200", "request": "^2.88.2", "request-promise": "^4.2.5", }, "devDependencies": { "firebase-functions-test": "^0.1.6" }, "private": true }
问题示例PDF链接
- https://www.nea.gov.sg/docs/default-source/our-services/building-planning/notification-of-new-edition-of-code-of-practice-on-environment-health-(2020-edition).pdf
- https://www.nparks.gov.sg/-/media/nparks-real-content/partner-us/developers-architects-and-engineers/circular_2020_0106_nparks.pdf?la=en&hash=F25A74CC8667D5D98EDF3A9C186E235330D228A8
解决方案
这个问题我之前也碰到过类似的场景,核心原因大概率是旧Node.js版本的根证书库过时,或者PDFJS内置的HTTP客户端在Cloud Functions环境下的SSL验证兼容性问题。给你几个可行的解决思路:
1. 升级Node.js到支持的LTS版本
你现在用的Node.js 8版本早就停止维护了,它内置的根证书列表已经很久没更新,就算目标网站的SSL证书有效,旧版本的Node也可能无法识别。Firebase Cloud Functions目前支持Node.js 14、16、18这些LTS版本,建议直接升级到最新的稳定版:
- 修改
package.json里的engines字段:"engines": { "node": "18" } - 同时把
firebase-functions和firebase-admin也升级到兼容新版本Node的最新版本,避免版本冲突:npm install firebase-functions@latest firebase-admin@latest
2. 手动下载PDF后再传给PDFJS处理
PDFJS自带的HTTP客户端在Cloud Functions环境下可能存在SSL验证的bug,你可以先用axios(你项目里已经依赖了)提前把PDF下载到内存,再用Buffer给PDFJS处理,绕开它的内置请求逻辑:
修改extractPlainTextFromPdf函数如下:
const axios = require('axios'); async function extractPlainTextFromPdf(pdfUrl) { try { // 先下载PDF文件到Buffer const response = await axios.get(pdfUrl, { responseType: 'arraybuffer', headers: { "User-Agent": "MY-USER-AGENT" } }); // 用Buffer创建PDFJS的文档选项 const options = { data: new Uint8Array(response.data), httpHeaders: { "User-Agent": "MY-USER-AGENT" } }; return getPlainBody(options); } catch (err) { console.log("Err downloading PDF", err); throw err; } }
axios的HTTP客户端根证书更新更及时,能更好地处理现代SSL证书验证。
3. 更新PDFJS到最新稳定版
你当前使用的pdfjs-dist@2.3.200是2020年的版本,后续版本修复了不少网络请求相关的bug,包括SSL验证的问题。直接升级到最新版试试:
npm install pdfjs-dist@latest
升级后重新部署测试,很多时候这类环境兼容性问题在新版本里已经被解决了。
4. 额外排查:检查Cloud Functions环境的根证书
虽然概率较低,但如果上面的方法都不行,可以确认下Cloud Functions运行环境的根证书是否正常。不过升级Node.js版本通常会同步更新根证书库,所以这个步骤可以作为最后的排查手段。
内容的提问来源于stack exchange,提问作者Koh

