You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。

核心思路

  1. 前端把签名画布转成base64图片,传给后端
  2. 后端用Jinja2渲染包含签名图片的HTML模板
  3. 用WeasyPrint把HTML转成PDF并存库
方案对比与推荐
方案优点缺点推荐场景
Headless Chrome1:1还原Chrome打印效果服务器需安装Chrome,资源占用略高对PDF样式还原度要求高
前端html2pdf.js部署简单,无后端浏览器依赖样式还原度略逊轻量场景,样式简单
WeasyPrint纯Python栈,无浏览器依赖CSS兼容性差,样式调整成本高轻量静态页面场景

个人最推荐Headless Chrome方案,完全匹配你提到的Chrome打印完美效果的需求,只要搞定服务器环境配置,后续维护成本很低。

内容的提问来源于stack exchange,提问作者Jatin Tek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:57:38