之前的网页截图生成 PDF 项目使用的是 PhantomJS,由于其目前已停止开发,故切换到 puppeteer。
运行环境准备
关于如何在 docker 中运行 puppeteer,官方文档中有详细说明(@Reference 1),这里仅以在 alpine 环境中运行举例。
FROM alpine
# Replace apk install source for Chinese users.
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# Installs latest Chromium (100) package.
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-dejavu \
fontconfig \
nodejs \
npm
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Puppeteer v13.5.0 works with Chromium 100.
RUN yarn add puppeteer@13.5.0
# Add user so we don't need --no-sandbox.
RUN addgroup -S pptruser && adduser -S -G pptruser pptruser \
&& mkdir -p /home/pptruser/Downloads /app \
&& chown -R pptruser:pptruser /home/pptruser \
&& chown -R pptruser:pptruser /app
# Run everything after as non-privileged user.
USER pptruser
这里需要 注意 的点:
- 只能够使用 apk 安装的 chromium,puppeteer 内置的脚本安装的 chromium 会有动态链接库不兼容的问题,因此一定要配置好 npm 安装依赖时的环境变量
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
和运行时的环境变量PUPPETEER_EXECUTABLE_PATH
。 - 目前通过 apk 安装的 chromium 只支持在 puppeteer@13.5.0 下运行,因此需要检查好安装的 puppeteer 版本。
- 由于 alpine 中不含字体库,会导致 puppeteer 无法正常渲染文字,故添加
fontconfig
用于安装字体,需要将字体文件放置在/usr/share/fonts/
目录下。 - 国内用户使用 apk 安装库时可能会遇到网络问题,故可以切换 apk 源到国内镜像。
生成 PDF 示例代码
import puppeteer from 'puppeteer';
type Options = puppeteer.PDFOptions & {};
async function genPDF(content: string, options: Options) {
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#tips
'--disable-dev-shm-usage',
],
});
const page = await browser.newPage();
await page.setContent(content);
const pdfBuffer = await page.pdf(options);
await browser.close();
return pdfBuffer;
}