Sign inSign up

julezhou/playwright

By julezhou

•Updated 9 months ago

基于Ubuntu20.04构建的playwright,基于node js脚本做接口

Image
API management
Developer tools
0

440

julezhou/playwright repository overview

  1. 定义Ubuntu源为阿里云
    sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    	sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    	apt-get clean && apt-get update -y
    
  2. 安装依赖
    apt-get install -y --no-install-recommends \
    	curl \
    	ca-certificates \
    	gnupg \
    	unzip \
    	wget \
    	fontconfig \
    	fonts-noto-cjk \
    	fonts-liberation \
    	libnss3 \
    	libatk1.0-0 \
    	libatk-bridge2.0-0 \
    	libcups2 \
    	libdrm2 \
    	libxkbcommon0 \
    	libxcomposite1 \
    	libxdamage1 \
    	libxfixes3 \
    	libxrandr2 \
    	libgbm1 \
    	libasound2 \
    	libpangocairo-1.0-0 \
    	libpango-1.0-0 \
    	libgtk-3-0 \
    	libxshmfence1 && \
    	rm -rf /var/lib/apt/lists/*
    
  3. node安装及换源
    curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
    	apt-get update -y && apt-get install -y nodejs && \
    	rm -rf /var/lib/apt/lists/* && \
    	npm config set registry https://registry.npmmirror.com
    
  4. 定义依赖文件
    {
      "name": "render-service",
      "private": true,
      "type": "module",
      "dependencies": {
    	"express": "^4.19.2",
    	"playwright": "1.43.0"
      }
    }
    
  5. 安装npm及playwright
    if [ -f package.json ]; then npm install; fi && \
    	npx playwright install chromium --with-deps
    
  6. 服务端口号为3000,在server.js中确定
  7. 启动命令为 node server.js
  8. 接口说明
    说明请求方式地址参数响应
    转pdfPOST/render/pdf<html源码>result.pdf
    转PNGPOST/render/png<html源码>result.png
  9. 构建文件说明-Dockerfile
    FROM ubuntu:22.04
    LABEL authors="Jule"
    
    # 核心修复1:配置阿里云源(转义符+语法规范)
    RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    	sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    	apt-get clean && apt-get update -y
    
    # 核心修复2:安装基础依赖(单独RUN,避免命令串联错误)
    RUN apt-get install -y --no-install-recommends \
    	curl \
    	ca-certificates \
    	gnupg \
    	unzip \
    	wget \
    	fontconfig \
    	fonts-noto-cjk \
    	fonts-liberation \
    	libnss3 \
    	libatk1.0-0 \
    	libatk-bridge2.0-0 \
    	libcups2 \
    	libdrm2 \
    	libxkbcommon0 \
    	libxcomposite1 \
    	libxdamage1 \
    	libxfixes3 \
    	libxrandr2 \
    	libgbm1 \
    	libasound2 \
    	libpangocairo-1.0-0 \
    	libpango-1.0-0 \
    	libgtk-3-0 \
    	libxshmfence1 && \
    	rm -rf /var/lib/apt/lists/*
    
    # 核心修复3:安装Node.js(单独RUN,避免和基础依赖冲突)
    RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
    	apt-get update -y && apt-get install -y nodejs && \
    	rm -rf /var/lib/apt/lists/* && \
    	# 配置国内npm源,解决npm install慢/失败
    	npm config set registry https://registry.npmmirror.com
    
    # 核心修复4:工作目录+业务文件拷贝(规范执行顺序)
    WORKDIR /app
    COPY package.json ./
    COPY server.js ./
    
    # 核心修复5:安装npm依赖+Playwright(有package.json才执行,避免报错)
    RUN if [ -f package.json ]; then npm install; fi && \
    	npx playwright install chromium --with-deps
    
    # 业务环境变量
    ENV BROWSER_WORKERS=2
    ENV MAX_CONTEXTS=4
    
    # 暴露端口
    EXPOSE 3000
    
    # 启动命令
    CMD ["node", "server.js"]
    
  10. 构建文件说明-package.json
    {
      "name": "render-service",
      "private": true,
      "type": "module",
      "dependencies": {
    	"express": "^4.19.2",
    	"playwright": "1.43.0"
      }
    }
    
  11. 构建文件说明-server.js
    import express from "express";
    import { chromium } from "playwright";
    
    const app = express();
    app.use(express.text({ limit: "10mb", type: "*/*" }));
    
    const PORT = 3000;
    const BROWSER_WORKERS = parseInt(process.env.BROWSER_WORKERS || "2");
    const MAX_CONTEXTS = parseInt(process.env.MAX_CONTEXTS || "4");
    
    /**
     * Browser Pool
     */
    const pool = [];
    
    class BrowserWorker {
    	constructor(browser) {
    		this.browser = browser;
    		this.availableContexts = MAX_CONTEXTS;
    	}
    }
    
    async function initBrowserPool() {
    	console.log(`🚀 Starting ${BROWSER_WORKERS} Chromium browsers...`);
    
    	for (let i = 0; i < BROWSER_WORKERS; i++) {
    		const browser = await chromium.launch({
    			args: [
    				"--no-sandbox",
    				"--disable-dev-shm-usage",
    				"--disable-gpu"
    			]
    		});
    		pool.push(new BrowserWorker(browser));
    		console.log(`✅ Browser ${i} ready`);
    	}
    }
    
    /**
     * Acquire a browser slot (blocking)
     */
    async function acquireWorker() {
    	while (true) {
    		for (const worker of pool) {
    			if (worker.availableContexts > 0) {
    				worker.availableContexts--;
    				return worker;
    			}
    		}
    		await new Promise(r => setTimeout(r, 10));
    	}
    }
    
    function releaseWorker(worker) {
    	worker.availableContexts++;
    }
    
    /**
     * Render PDF
     */
    app.post("/render/pdf", async (req, res) => {
    	const html = req.body;
    	if (!html) {
    		return res.status(400).send("HTML content required");
    	}
    
    	const worker = await acquireWorker();
    	const context = await worker.browser.newContext();
    	const page = await context.newPage();
    
    	try {
    		await page.setContent(html, { waitUntil: "networkidle" });
    		const pdf = await page.pdf({ format: "A4" });
    
    		res.setHeader("Content-Type", "application/pdf");
    		res.setHeader("Content-Disposition", "inline; filename=result.pdf");
    		res.send(pdf);
    	} catch (err) {
    		console.error(err);
    		res.status(500).send("Render PDF failed");
    	} finally {
    		await page.close();
    		await context.close();
    		releaseWorker(worker);
    	}
    });
    
    /**
     * Render PNG
     */
    app.post("/render/png", async (req, res) => {
    	const html = req.body;
    	if (!html) {
    		return res.status(400).send("HTML content required");
    	}
    
    	const worker = await acquireWorker();
    	const context = await worker.browser.newContext({
    		viewport: { width: 1920, height: 1080 }
    	});
    	const page = await context.newPage();
    
    	try {
    		await page.setContent(html, { waitUntil: "networkidle" });
    		const image = await page.screenshot({ fullPage: true });
    
    		res.setHeader("Content-Type", "image/png");
    		res.send(image);
    	} catch (err) {
    		console.error(err);
    		res.status(500).send("Render PNG failed");
    	} finally {
    		await page.close();
    		await context.close();
    		releaseWorker(worker);
    	}
    });
    
    /**
     * Health check
     */
    app.get("/health", (_, res) => {
    	res.json({
    		status: "UP",
    		browsers: BROWSER_WORKERS,
    		maxContextsPerBrowser: MAX_CONTEXTS
    	});
    });
    
    /**
     * Start server
     */
    (async () => {
    	await initBrowserPool();
    	app.listen(PORT, () => {
    		console.log(`✅ Render service listening on :${PORT}`);
    	});
    })();
    

Tag summary

Content type

Image

Digest

sha256:ea643a2b9…

Size

552.4 MB

Last updated

9 months ago

docker pull julezhou/playwright