函数计算(FC)集成Puppeteer

函数计算(FC)集成Puppeteer实战:Serverless化HTML转图片/PDF

你想在函数计算(FC)中使用Puppeteer实现HTML转图片/PDF的功能——这是典型的Serverless场景(按需执行、弹性扩缩),但FC默认环境缺少Chrome运行依赖,需要针对性适配。下面以阿里云FC(国内最常用)为例,从环境适配、完整代码、配置优化、避坑点全维度讲解,代码可直接部署运行。

一、核心前提:FC中使用Puppeteer的关键问题

函数计算(无论阿里云FC/AWS Lambda/腾讯SCF)的默认运行环境是极简的,而Puppeteer依赖Chrome/Chromium运行时+系统底层库,核心要解决2个问题:

  1. 依赖缺失:FC环境缺少libatk、libgtk等Chrome依赖库,直接运行会报错;
  2. 资源限制:Puppeteer启动Chrome需要足够内存(至少512MB)和超时时间;
  3. 冷启动优化:Chrome启动耗时久,需通过“层/预留实例”降低冷启动延迟。

二、环境适配:安装Puppeteer与系统依赖

1. 选择FC运行时(推荐Node.js)

优先选Node.js 14+/16+运行时(Puppeteer对Node.js兼容性最好),以下以阿里云FC Node.js 16环境为例。

2. 解决系统依赖(核心)

阿里云FC的Linux环境(Alpine/CentOS)缺少Chrome依赖,需通过「自定义层」或「代码包内置依赖」解决,推荐用(复用性更高):

步骤1:本地构建Chrome依赖层

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 创建层目录
mkdir -p fc-puppeteer-layer/nodejs
cd fc-puppeteer-layer/nodejs

# 2. 安装Puppeteer(指定下载国内镜像,避免网络问题)
PUPPETEER_DOWNLOAD_HOST=https://npm.taobao.org/mirrors \
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=false \
npm install puppeteer@19.8.0 # 选稳定版本,避免新版兼容问题

# 3. 打包层(压缩为zip)
cd ..
zip -r puppeteer-layer.zip nodejs

步骤2:上传层到阿里云FC

  1. 登录阿里云FC控制台 → 「层管理」→ 「创建层」;
  2. 上传刚打包的puppeteer-layer.zip,运行时选Node.js 16;
  3. 记录层的ARN,后续函数配置时关联。

补充:极简方案(代码包内置依赖)

若不想创建层,可直接在函数代码目录安装Puppeteer,打包后上传:

1
2
3
4
# 在函数代码目录执行
PUPPETEER_DOWNLOAD_HOST=https://npm.taobao.org/mirrors npm install puppeteer
# 打包所有文件(包括node_modules)为zip,上传到FC
zip -r fc-puppeteer.zip .

三、完整实战:阿里云FC中Puppeteer实现HTML转PDF/图片

1. 函数代码编写(index.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');

// 函数入口(阿里云FC标准格式)
exports.handler = async (event, context, callback) => {
// 解析请求参数(支持URL/HTML字符串)
const params = JSON.parse(event.toString() || '{}');
const { type = 'pdf', url = 'https://www.baidu.com', html } = params;

let browser = null;
try {
// 1. 启动无头Chrome(适配FC环境的关键配置)
browser = await puppeteer.launch({
// 核心:禁用沙箱(FC环境无沙箱权限)
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage', // 解决FC /dev/shm空间不足问题
'--disable-gpu', // 无GPU环境,禁用加速
'--single-process' // 单进程模式,减少资源占用
],
headless: 'new', // 新版无头模式,更轻量
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || null // 自动识别Chrome路径
});

// 2. 创建页面并渲染内容
const page = await browser.newPage();
// 设置页面尺寸(适配PDF/图片)
await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 2 });

// 渲染内容:优先用传入的HTML字符串,否则用URL
if (html) {
await page.setContent(html, { waitUntil: 'domcontentloaded' });
} else {
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
}

// 3. 生成PDF/图片
let result = null;
if (type === 'pdf') {
// 生成PDF(返回Buffer)
result = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' }
});
} else if (type === 'image') {
// 生成图片(返回Buffer)
result = await page.screenshot({
fullPage: true,
type: 'png',
quality: 100
});
}

// 4. 返回结果(可直接返回Buffer,或上传到OSS后返回URL)
callback(null, {
statusCode: 200,
headers: { 'Content-Type': type === 'pdf' ? 'application/pdf' : 'image/png' },
body: result.toString('base64'), // FC返回base64,前端解码即可
isBase64Encoded: true
});
} catch (err) {
console.error('执行失败:', err);
callback(null, {
statusCode: 500,
body: JSON.stringify({ error: err.message })
});
} finally {
// 确保关闭浏览器,释放资源
if (browser) await browser.close();
}
};

2. FC函数配置(关键)

在阿里云FC控制台配置函数:

配置项 推荐值 原因
运行时 Node.js 16 兼容Puppeteer,稳定性高
内存 1024 MB(最低512MB) Chrome启动需要足够内存
超时时间 30秒(最低10秒) 页面渲染+PDF生成需耗时
关联之前创建的puppeteer层 复用Chrome依赖,减小代码包
执行角色 配置AliyunOSSFullAccess(可选) 若需上传PDF/图片到OSS

3. 测试调用(示例请求)

通过FC控制台「测试函数」,传入以下JSON参数:

1
2
3
4
{
"type": "pdf",
"url": "https://www.baidu.com"
}

返回结果为base64编码的PDF,前端可通过atob()解码后下载/预览。

四、核心优化:解决FC+Puppeteer的冷启动问题

Puppeteer启动Chrome耗时约1-3秒,FC冷启动时会导致请求延迟高,优化方案:

1. 配置「预留实例」(推荐)

在阿里云FC控制台→函数→「配置」→「弹性配置」→开启预留实例:

  • 预留1-2个实例,确保高并发时直接使用预热实例,无冷启动;
  • 成本略增加,但响应延迟可降到100ms内。

2. 优化Chrome启动参数

puppeteer.launch中添加以下参数,减少启动耗时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--single-process',
'--no-zygote', // 禁用zygote进程,减少启动时间
'--disable-extensions', // 禁用扩展
'--disable-background-timer-throttling'
],
headless: 'new', // 新版无头模式比旧版快50%
ignoreDefaultArgs: ['--disable-extensions'], // 忽略默认扩展
slowMo: 0 // 禁用慢动作
});

3. 减小函数包体积

  • 仅保留Puppeteer必要依赖,删除node_modules中无用文件;
  • 使用puppeteer-core替代puppeteer,手动指定Chrome路径(需提前在FC环境安装Chrome)。

五、避坑指南:FC+Puppeteer常见问题

1. 报错「No usable sandbox!」

→ 解决方案:启动Chrome时添加--no-sandbox --disable-setuid-sandbox参数(已在代码中配置)。

2. 报错「/dev/shm too small」

→ 解决方案:添加--disable-dev-shm-usage参数,Chrome会使用/tmp而非/dev/shm。

3. 中文乱码/字体缺失

→ 解决方案:

  1. 在层中添加中文字体(如思源黑体);
  2. 代码中指定字体路径:
    1
    2
    3
    4
    5
    6
    await page.emulateMediaType('screen');
    await page.addStyleTag({
    content: `
    body { font-family: 'SimHei', 'Microsoft YaHei', sans-serif !important; }
    `
    });

4. 内存不足崩溃

→ 解决方案:将FC函数内存调高到1024MB或2048MB,Chrome渲染大页面需要足够内存。

5. 超时错误

→ 解决方案:

  1. 调高FC函数超时时间(最大支持10分钟);
  2. 优化页面加载:waitUntil改为domcontentloaded,减少等待时间。

六、总结

关键点回顾

  1. 核心适配:FC中使用Puppeteer需解决Chrome系统依赖(通过层/内置依赖),必须添加--no-sandbox等启动参数;
  2. 配置要求:函数内存≥512MB、超时≥10秒,否则Chrome无法启动/执行超时;
  3. 冷启动优化:预留实例+精简Chrome启动参数,是降低响应延迟的核心手段;
  4. 避坑重点:解决沙箱、/dev/shm、字体缺失、内存不足等问题,确保稳定运行。

函数计算+Puppeteer是Serverless化处理HTML转PDF/图片的最佳方案,既能利用FC的弹性扩缩容承接高并发,又能按需计费(无请求时零成本)。如果需要将生成的文件自动上传到OSS,或批量处理URL,可基于上述代码扩展(添加OSS SDK调用逻辑即可)。


函数计算(FC)集成Puppeteer
https://cszy.top/2019-09-30-函数计算/
作者
csorz
发布于
2019年9月30日
许可协议