news 2026/2/24 18:43:54

Qwen-Image-2512-ComfyUI API集成:Flask调用封装代码实例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qwen-Image-2512-ComfyUI API集成:Flask调用封装代码实例

Qwen-Image-2512-ComfyUI API集成:Flask调用封装代码实例

1. 为什么需要把ComfyUI变成API服务

你有没有遇到过这样的情况:在ComfyUI界面里点点选选,生成一张图很顺手,但想把它嵌进自己的网页、小程序或者自动化流程里,就卡住了?ComfyUI默认是图形界面,不直接对外提供HTTP接口。而真实项目中,我们往往需要让图片生成功能“被调用”——比如电商后台批量生成商品图、内容平台自动配图、甚至接进企业微信机器人发图。

这时候,Qwen-Image-2512-ComfyUI这个镜像就很有价值:它已经预装了阿里最新版的Qwen-Image模型(2512版本),还集成了ComfyUI环境,4090D单卡就能跑起来。但光有镜像还不够,得让它“听懂”程序发来的请求。本文不讲怎么从零搭环境,也不重复部署步骤,而是聚焦一个工程师真正要落地的事:用Flask写一套轻量、稳定、可复用的API封装层,把ComfyUI变成你随时能POST调用的服务

整套方案完全本地运行,不依赖云服务,代码简洁(不到150行),支持传入提示词、尺寸、采样步数等常用参数,返回生成图的URL或base64。你不需要改ComfyUI源码,也不用碰Node.js,只要会写Python,就能快速接入。

2. 环境准备与服务结构设计

2.1 镜像基础确认

你已按说明完成镜像部署:

  • 在4090D单卡机器上拉起镜像;
  • 运行/root/1键启动.sh
  • 通过“我的算力”进入 ComfyUI 网页端,确认工作流可正常出图。

这说明后端服务(ComfyUI的/prompt接口)已在本地http://127.0.0.1:8188运行。这是整个API封装的前提——我们不是重写推理,而是做一层“翻译+调度”。

2.2 Flask服务定位与职责划分

我们不替代ComfyUI,而是站在它肩膀上构建能力:

模块职责你不用管的你需要控制的
ComfyUI原生服务执行节点计算、加载模型、渲染图像模型权重路径、显存分配、节点逻辑仅需确保其/prompt接口可访问
Flask API层接收HTTP请求 → 校验参数 → 构造ComfyUI Prompt JSON → 发送请求 → 轮询结果 → 返回图片请求超时重试策略、日志格式、错误码定义请求字段映射、默认值设定、响应结构

简单说:Flask是“前台接待员”,ComfyUI是“后台画师”。接待员负责听懂客户说什么(解析JSON)、填好工单(构造Prompt)、催进度(轮询)、最后把画作交到客户手上(返回图片)。

2.3 依赖安装与目录结构

在镜像内执行(建议在/root下新建qwen-api目录):

pip install flask requests pillow

推荐目录结构如下(清晰、易维护):

qwen-api/ ├── app.py # 主服务入口 ├── workflow_api.json # 从ComfyUI导出的API工作流(关键!) ├── utils.py # 图片处理、路径管理等工具函数 └── static/ └── outputs/ # 自动生成的图片存放目录(ComfyUI默认输出路径)

注意workflow_api.json不是手写的,而是从ComfyUI网页端导出的。打开内置工作流 → 点右上角「Queue Prompt」旁的「Save as API」→ 下载JSON文件,放入项目根目录。这是保证API调用和界面操作效果一致的核心。

3. 核心代码实现详解

3.1 工作流JSON的关键字段解析

打开你导出的workflow_api.json,找到类似这样的节点:

"6": { "inputs": { "text": "A cat wearing sunglasses, summer vibe", "clip": ["12", 1] }, "class_type": "CLIPTextEncode" }, "8": { "inputs": { "width": 1024, "height": 1024, "batch_size": 1 }, "class_type": "KSampler" }

我们要动态替换的,就是这些inputs里的值。Flask收到请求后,会提取promptwidthheight等参数,精准注入到对应节点ID(如68)的inputs中。不是全文本替换,而是基于节点ID的字典级更新——这才是稳定可靠的做法。

3.2 Flask主服务(app.py)

# app.py from flask import Flask, request, jsonify, send_file import requests import json import time import os from pathlib import Path from utils import get_image_path, save_workflow_with_params app = Flask(__name__) # ComfyUI服务地址(镜像内默认) COMFYUI_URL = "http://127.0.0.1:8188" # 加载原始工作流模板 with open("workflow_api.json", "r", encoding="utf-8") as f: WORKFLOW_TEMPLATE = json.load(f) @app.route("/generate", methods=["POST"]) def generate_image(): try: data = request.get_json() prompt = data.get("prompt", "").strip() if not prompt: return jsonify({"error": "prompt is required"}), 400 # 提取参数,设置默认值 width = int(data.get("width", 1024)) height = int(data.get("height", 1024)) steps = int(data.get("steps", 30)) seed = int(data.get("seed", -1)) # 动态注入参数到工作流(核心逻辑) workflow = save_workflow_with_params( WORKFLOW_TEMPLATE, prompt=prompt, width=width, height=height, steps=steps, seed=seed ) # 发送Prompt请求 resp = requests.post( f"{COMFYUI_URL}/prompt", json={"prompt": workflow}, timeout=5 ) resp.raise_for_status() queue_resp = resp.json() prompt_id = queue_resp["prompt_id"] # 轮询获取结果(最多等待90秒) for _ in range(90): history = requests.get(f"{COMFYUI_URL}/history/{prompt_id}").json() if prompt_id in history and "outputs" in history[prompt_id]: output = list(history[prompt_id]["outputs"].values())[0] if "images" in output: img_info = output["images"][0] img_path = get_image_path(img_info) if os.path.exists(img_path): # 返回图片URL(假设Nginx已配置static代理) return jsonify({ "status": "success", "image_url": f"/static/outputs/{img_info['subfolder']}/{img_info['filename']}", "prompt_id": prompt_id }) time.sleep(1) return jsonify({"error": "timeout waiting for image"}), 504 except requests.exceptions.RequestException as e: return jsonify({"error": f"comfyui request failed: {str(e)}"}), 502 except Exception as e: return jsonify({"error": f"server error: {str(e)}"}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=False)

这段代码做了四件关键事:

  1. 安全校验:检查prompt非空,参数类型转换防崩;
  2. 精准注入:调用save_workflow_with_params,只改目标节点,不动其他逻辑;
  3. 异步等待:用/history/{id}轮询,避免长连接阻塞;
  4. 错误分层:网络失败返回502,超时返回504,代码异常返回500——便于前端区分处理。

3.3 参数注入工具(utils.py)

# utils.py import json import os from pathlib import Path def save_workflow_with_params(workflow, **kwargs): """根据节点ID,将参数注入到指定位置""" # CLIPTextEncode节点(通常ID为6或类似,按你导出的JSON调整) if "prompt" in kwargs and "6" in workflow: workflow["6"]["inputs"]["text"] = kwargs["prompt"] # KSampler节点(控制尺寸、步数、随机种子) if "8" in workflow: # 假设KSampler节点ID是8 if "width" in kwargs: workflow["8"]["inputs"]["width"] = kwargs["width"] if "height" in kwargs: workflow["8"]["inputs"]["height"] = kwargs["height"] if "steps" in kwargs: workflow["8"]["inputs"]["steps"] = kwargs["steps"] if "seed" in kwargs: workflow["8"]["inputs"]["seed"] = kwargs["seed"] # 其他节点可依此类推(如VAEDecode、SaveImage等) return workflow def get_image_path(img_info): """根据ComfyUI返回的image info,拼出本地绝对路径""" base_dir = "/root/comfyui/output" # ComfyUI默认输出目录 subfolder = img_info.get("subfolder", "") filename = img_info["filename"] return os.path.join(base_dir, subfolder, filename)

关键提醒:节点ID(如"6""8")必须和你导出的workflow_api.json中实际ID严格一致。打开JSON文件,搜索"class_type": "CLIPTextEncode""class_type": "KSampler",看它们前面的数字ID是多少,然后在utils.py里对应修改。这是唯一需要你手动核对的地方。

4. 启动服务与调用验证

4.1 启动Flask服务

qwen-api/目录下执行:

nohup python app.py > api.log 2>&1 &

服务将在http://你的服务器IP:5000/generate监听POST请求。

4.2 用curl测试(最简验证)

curl -X POST http://localhost:5000/generate \ -H "Content-Type: application/json" \ -d '{ "prompt": "a cyberpunk cityscape at night, neon lights, rain, cinematic", "width": 1024, "height": 576, "steps": 25 }'

成功响应示例:

{ "status": "success", "image_url": "/static/outputs/ComfyUI_2024-06-15/IMG_00001.png", "prompt_id": "abc123def456" }

4.3 前端调用示例(JavaScript)

async function callQwenAPI(prompt) { const res = await fetch("http://your-server-ip:5000/generate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: prompt, width: 1024, height: 1024 }) }); const data = await res.json(); if (data.status === "success") { document.getElementById("result-img").src = data.image_url; } }

5. 实用技巧与避坑指南

5.1 如何让生成图直接返回base64(免静态服务)

如果你不想配Nginx代理静态文件,想让API直接返回图片数据,只需修改app.py中成功分支:

# 替换原来的 jsonify(...) 部分 with open(img_path, "rb") as f: img_data = f.read() import base64 encoded = base64.b64encode(img_data).decode("utf-8") return jsonify({ "status": "success", "image_base64": f"data:image/png;base64,{encoded}", "prompt_id": prompt_id })

这样前端拿到base64字符串,直接赋给<img src="...">即可显示,零配置。

5.2 多模型切换支持(扩展思路)

Qwen-Image-2512只是起点。ComfyUI支持加载多个模型。你可以在workflow_api.json中,把模型加载节点(如CheckpointLoaderSimple)的ckpt_name字段,改为一个变量占位符,然后在save_workflow_with_params里根据请求参数动态替换:

# 在workflow中预留 "4": { "inputs": { "ckpt_name": "{{model_name}}" }, "class_type": "CheckpointLoaderSimple" } # 注入时 if "model_name" in kwargs: workflow["4"]["inputs"]["ckpt_name"] = kwargs["model_name"]

再配合一个/models接口返回可用模型列表,就能做成真正的多模型API网关。

5.3 常见问题速查

  • Q:调用返回502,但ComfyUI网页能出图?
    A:检查COMFYUI_URL是否正确(必须是127.0.0.1:8188,不能写localhost或外网IP);确认/root/comfyui目录下output子目录有写入权限。

  • Q:轮询一直超时,history里没outputs?
    A:打开ComfyUI网页,看右下角WebSocket状态是否绿色;检查workflow_api.jsonSaveImage节点的filename_prefix是否为ComfyUI(默认值),否则路径拼接会失败。

  • Q:中文提示词乱码或不生效?
    A:确保workflow_api.json中CLIPTextEncode节点的text字段是UTF-8编码;Flask接收JSON时加request.get_json(force=True)强制解码。

6. 总结

本文带你走通了一条从“能用”到“好用”的关键路径:Qwen-Image-2512-ComfyUI镜像本身解决了模型和环境问题,而Flask API封装则解决了工程集成问题。你学到的不是一段孤立代码,而是一种可复用的方法论——

  • 不侵入原系统:所有改动都在API层,ComfyUI保持原样;
  • 强可维护性:参数注入逻辑集中,增删字段只需改utils.py
  • 真生产就绪:包含超时控制、错误分类、路径安全校验;
  • 平滑可扩展:base64返回、多模型支持、鉴权接入,都只需几行代码。

下一步,你可以把这套API注册进你的内部服务发现系统,加上Prometheus监控,再用Swagger生成文档——Qwen-Image就真正成为你技术栈里一个标准的AI能力模块了。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/11 7:56:18

cv_resnet18_ocr-detection性能调优:输入尺寸与速度平衡实战

cv_resnet18_ocr-detection性能调优&#xff1a;输入尺寸与速度平衡实战 1. 模型背景与核心价值 1.1 为什么需要关注输入尺寸&#xff1f; OCR文字检测不是“越大越好”的简单逻辑。cv_resnet18_ocr-detection 这个模型&#xff0c;名字里就藏着关键线索&#xff1a;它基于 …

作者头像 李华
网站建设 2026/2/23 13:09:21

4步精通SO100机器人仿真开发:从URDF模型解析到环境部署全指南

4步精通SO100机器人仿真开发&#xff1a;从URDF模型解析到环境部署全指南 【免费下载链接】SO-ARM100 Standard Open Arm 100 项目地址: https://gitcode.com/GitHub_Trending/so/SO-ARM100 机器人仿真开发是快速验证机械设计和控制算法的关键环节&#xff0c;而URDF模型…

作者头像 李华
网站建设 2026/2/18 14:40:56

YOLOv9锚框设计:无Anchor机制原理简析

YOLOv9锚框设计&#xff1a;无Anchor机制原理简析 YOLO系列模型从v1到v8&#xff0c;一直依赖Anchor&#xff08;锚框&#xff09;作为目标检测的先验基础——通过预设一组宽高比和尺度的框&#xff0c;在特征图上密集预测偏移量。但YOLOv9彻底打破了这一惯例&#xff1a;它不…

作者头像 李华
网站建设 2026/2/22 13:12:53

SGLang如何减少重复计算?看完你就明白了

SGLang如何减少重复计算&#xff1f;看完你就明白了 在大模型推理服务的实际部署中&#xff0c;你是否遇到过这样的问题&#xff1a;多轮对话时每次都要重新计算前面几轮的提示词&#xff08;prompt&#xff09;&#xff1f;长上下文场景下KV缓存反复加载、显存占用飙升、首To…

作者头像 李华
网站建设 2026/2/22 18:06:28

Qwen-Image-2512-ComfyUI镜像维护:版本升级与回滚操作指南

Qwen-Image-2512-ComfyUI镜像维护&#xff1a;版本升级与回滚操作指南 1. 为什么需要关注镜像版本维护 你刚用Qwen-Image-2512-ComfyUI生成了一张惊艳的电商主图&#xff0c;正准备批量部署到团队工作流中&#xff0c;突然发现新发布的模型补丁修复了关键的构图偏移问题——但…

作者头像 李华
网站建设 2026/2/20 17:02:26

离线语音识别:无需联网的20+语言实时转写方案

离线语音识别&#xff1a;无需联网的20语言实时转写方案 【免费下载链接】vosk-api vosk-api: Vosk是一个开源的离线语音识别工具包&#xff0c;支持20多种语言和方言的语音识别&#xff0c;适用于各种编程语言&#xff0c;可以用于创建字幕、转录讲座和访谈等。 项目地址: h…

作者头像 李华