Flask带签名画布页面生成PDF并存储至MySQL方案咨询
嘿,这个需求我之前帮人处理过类似的,结合你提到Chrome手动打印PDF的效果完美,咱们完全可以把这个流程自动化,同时适配服务器部署场景。下面给你几个可行的方案,从贴合你现有使用习惯的到轻量替代的都有:
方案一:Headless Chrome + Flask 后端调用(最贴合你的需求)
既然Chrome原生打印效果达标,那直接用Headless模式的Chrome来自动化这个流程是最优解,能1:1还原手动打印的效果。
步骤1:服务器环境准备
- 安装Chromium/Chrome:Ubuntu服务器可以用
sudo apt install chromium-browser,CentOS或其他发行版对应调整。 - 选择Python库:推荐用
pyppeteer(Python版的Puppeteer,无需额外装chromedriver),或者selenium配合chromedriver。
步骤2:核心代码实现
Flask后端(生成PDF并存库)
from flask import Flask, request, jsonify from pyppeteer import launch from sqlalchemy import create_engine, Column, Integer, LargeBinary, DateTime, func from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker import asyncio app = Flask(__name__) # 数据库配置(替换成你的MySQL信息) DB_URL = "mysql+pymysql://username:password@host:port/db_name" engine = create_engine(DB_URL) Base = declarative_base() # 定义数据库模型 class SignedDocument(Base): __tablename__ = "signed_documents" id = Column(Integer, primary_key=True, autoincrement=True) pdf_data = Column(LargeBinary, nullable=False) created_at = Column(DateTime, default=func.now()) Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) @app.route("/generate-and-save-pdf", methods=["POST"]) def generate_pdf(): # 前端传带签名的页面URL(或者直接传页面HTML,按需调整) page_url = request.json.get("page_url") if not page_url: return jsonify({"error": "缺少页面URL参数"}), 400 # 异步执行Headless Chrome操作 async def run(): # 启动Headless Chrome,添加参数避免权限/内存问题 browser = await launch( headless=True, args=["--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"] ) page = await browser.newPage() # 等待页面完全加载(包括签名画布渲染) await page.goto(page_url, waitUntil="networkidle2") # 生成PDF,模拟Chrome打印配置 pdf_bytes = await page.pdf({ "format": "A4", "printBackground": True, # 确保背景样式、画布都被打印 "margin": {"top": "1cm", "right": "1cm", "bottom": "1cm", "left": "1cm"} }) await browser.close() return pdf_bytes pdf_bytes = asyncio.run(run()) # 存储PDF到MySQL session = Session() new_doc = SignedDocument(pdf_data=pdf_bytes) session.add(new_doc) session.commit() doc_id = new_doc.id session.close() return jsonify({"message": "PDF生成并存储成功", "document_id": doc_id}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)
前端触发代码(签名完成后调用)
// 假设你的签名画布ID是signature-canvas async function finishSignAndSave() { // 确保签名已经渲染完成(可以加个状态判断) const currentPageUrl = window.location.href; const response = await fetch("/generate-and-save-pdf", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({page_url: currentPageUrl}) }); const result = await response.json(); console.log(result.message); }
部署注意事项
- 服务器需要安装Chrome依赖库:Ubuntu上可以用
sudo apt install libxss1 libappindicator1 libgconf-2-4 - 如果用Docker部署,可以用官方Python镜像+Chromium依赖,参考Dockerfile:
FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 安装Chromium及依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ chromium-browser \ libx11-xcb1 \ libxcb-dri3-0 \ libdrm2 \ && rm -rf /var/lib/apt/lists/* COPY . . CMD ["python", "app.py"]
方案二:前端生成PDF后上传(轻量无后端浏览器依赖)
如果不想在服务器装Chrome,可以用前端库html2pdf.js直接生成PDF再上传到后端,缺点是样式还原度可能略逊于Chrome原生打印,但胜在部署简单。
核心代码示例
前端生成并上传
function generateAndUploadPDF() { // 先把签名画布转成图片(避免画布无法被PDF捕获) const canvas = document.getElementById("signature-canvas"); const imgData = canvas.toDataURL("image/png"); const tempImg = document.createElement("img"); tempImg.src = imgData; tempImg.style.width = canvas.style.width; tempImg.style.height = canvas.style.height; canvas.parentNode.replaceChild(tempImg, canvas); // 生成PDF const targetElement = document.getElementById("page-container"); // 要生成PDF的页面容器 html2pdf().from(targetElement).set({ margin: 1, filename: "signed-document.pdf", image: {type: "png", quality: 0.98}, html2canvas: {scale: 2}, jsPDF: {unit: "cm", format: "A4", orientation: "portrait"} }).output("blob").then(pdfBlob => { // 上传到后端 const formData = new FormData(); formData.append("pdf_file", pdfBlob); fetch("/upload-pdf", {method: "POST", body: formData}) .then(res => res.json()) .then(result => { console.log("PDF上传成功"); // 还原签名画布 tempImg.parentNode.replaceChild(canvas, tempImg); }); }); }
后端接收路由
@app.route("/upload-pdf", methods=["POST"]) def upload_pdf(): if "pdf_file" not in request.files: return jsonify({"error": "未上传PDF文件"}), 400 pdf_file = request.files["pdf_file"] pdf_bytes = pdf_file.read() session = Session() new_doc = SignedDocument(pdf_data=pdf_bytes) session.add(new_doc) session.commit() doc_id = new_doc.id session.close() return jsonify({"message": "PDF存储成功", "document_id": doc_id})
方案三:纯Python PDF生成(WeasyPrint)
如果追求纯Python栈,不需要任何浏览器依赖,可以用WeasyPrint库。但需要注意它的CSS兼容性不如Chrome,需要调整页面样式来适配,签名画布同样要转成图片嵌入HTML。
核心思路
- 前端把签名画布转成base64图片,传给后端
- 后端用Jinja2渲染包含签名图片的HTML模板
- 用WeasyPrint把HTML转成PDF并存库
方案对比与推荐
| 方案 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| Headless Chrome | 1:1还原Chrome打印效果 | 服务器需安装Chrome,资源占用略高 | 对PDF样式还原度要求高 |
| 前端html2pdf.js | 部署简单,无后端浏览器依赖 | 样式还原度略逊 | 轻量场景,样式简单 |
| WeasyPrint | 纯Python栈,无浏览器依赖 | CSS兼容性差,样式调整成本高 | 轻量静态页面场景 |
个人最推荐Headless Chrome方案,完全匹配你提到的Chrome打印完美效果的需求,只要搞定服务器环境配置,后续维护成本很低。
内容的提问来源于stack exchange,提问作者Jatin Tek
相关产品推荐
相关产品推荐

