ComfyUI 工作流 API 怎么接入网站:前端、后端、队列和结果展示流程

面向网站开发者介绍 ComfyUI 工作流 API 怎么接入网站:如何设计前端表单、后端任务接口、图片上传、队列、状态轮询、结果存储、错误处理和上线保护。

ComfyUI 工作流 API 怎么接入网站,关键不只是会调用 /prompt。真正上线时,你要把一个生成工作流变成网站功能:用户上传图片或填写提示词,后端创建任务,ComfyUI 执行工作流,网站显示进度,生成结果保存到对象存储或本地文件服务,最后给用户一个可访问的结果页。

站内已经写过 RunningHub 这类云端工作流平台,也写过如何把 ComfyUI 工作流打包成 API。本文继续往开发接入层拆:如果你要把文生图、图生图、商品换背景、头像生成、海报生成、视频生成这类 ComfyUI 工作流放进自己的网站,应该怎么设计前后端。

先分清两层 API

接入网站时,不建议让前端直接调用 ComfyUI。更稳的结构是:

1
2
3
4
5
6
浏览器前端
  -> 你的网站后端 API
  -> 任务队列
  -> ComfyUI API
  -> 结果文件存储
  -> 网站结果页

也就是说,网站应该暴露自己的业务 API,而不是把 ComfyUI 的 /prompt/history/view 直接暴露给用户。

原因很简单:

  • 前端不能暴露 ComfyUI 地址和内部工作流 JSON;
  • 用户输入需要校验;
  • 生成任务需要排队和限流;
  • 输出文件需要统一存储和清理;
  • 失败、超时、退款、重试都属于业务逻辑;
  • 以后换 RunningHub、云 GPU 或其他生成服务时,前端不应该大改。

ComfyUI 是生成引擎,网站后端才是产品接口。

最小网站功能闭环

第一版可以只做这个闭环:

1
2
3
4
5
6
7
8
9
1. 用户填写 prompt 或上传图片
2. 前端请求 POST /api/generate
3. 后端创建 task_id
4. 后端把 workflow JSON 改成用户参数
5. 后端提交到 ComfyUI /prompt
6. 前端轮询 GET /api/tasks/{task_id}
7. 后端查询 ComfyUI /history
8. 生成完成后保存结果文件
9. 前端展示图片或视频

不要一开始就做复杂会员、积分、模板市场和批量任务。先跑通一次用户请求到结果展示。

后端接口怎么设计

网站后端可以先设计三个接口:

1
2
3
POST /api/generate
GET  /api/tasks/{task_id}
GET  /api/results/{result_id}

含义分别是:

  • POST /api/generate:创建生成任务;
  • GET /api/tasks/{task_id}:查询任务状态;
  • GET /api/results/{result_id}:访问生成结果。

一个创建任务请求可以长这样:

1
2
3
4
5
6
7
8
{
  "workflow": "product-background",
  "prompt": "white sneaker on a clean studio background",
  "negative_prompt": "blurry, low quality, watermark",
  "image_id": "upload_123",
  "width": 1024,
  "height": 1024
}

返回:

1
2
3
4
{
  "task_id": "task_20260710_001",
  "status": "queued"
}

前端拿到 task_id 后再轮询状态,不要让 /api/generate 一直阻塞到出图。

任务状态要自己维护

网站不要只依赖 ComfyUI 的 prompt_id。建议在你的数据库里维护一张任务表:

字段 说明
task_id 网站自己的任务 ID
user_id 用户 ID,未登录产品可为空
workflow 使用的工作流模板
status queued / running / succeeded / failed
comfy_prompt_id ComfyUI 返回的 prompt_id
input_params 用户输入参数
result_urls 生成结果地址
error_message 失败原因
created_at 创建时间
updated_at 更新时间

这样就算 ComfyUI 重启、网络失败或任务超时,你的网站也知道任务走到哪一步。

workflow JSON 不要让用户直接传

很多教程会直接把 workflow JSON 作为请求体提交。网站产品里不要这样做。

更安全的方式是后端维护白名单模板:

1
2
3
4
workflows/
  product-background.json
  avatar-generator.json
  poster-maker.json

用户只能选择模板和有限参数:

1
2
3
4
5
6
{
  "workflow": "poster-maker",
  "title": "Summer Sale",
  "style": "minimal",
  "size": "1024x1536"
}

后端再把这些参数写入固定节点。不要允许用户上传任意 workflow JSON,否则容易出现这些问题:

  • 调用未授权模型或节点;
  • 写入危险文件路径;
  • 消耗过高显存;
  • 生成超大文件;
  • 绕过你的计费和审核逻辑;
  • 让自定义节点执行不可控操作。

参数映射要单独维护

ComfyUI workflow 里节点 ID 经常是数字。不要在业务代码里到处写:

1
2
workflow["6"].inputs.text = prompt
workflow["3"].inputs.seed = seed

建议为每个模板维护一份映射:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "positive_prompt": {
    "node": "6",
    "field": "text"
  },
  "negative_prompt": {
    "node": "7",
    "field": "text"
  },
  "seed": {
    "node": "3",
    "field": "seed"
  },
  "input_image": {
    "node": "12",
    "field": "image"
  }
}

这样后面调整 workflow 时,只要改映射文件,不用到业务代码里找节点 ID。

图片上传怎么处理

图生图、换背景、换装、头像生成都会涉及图片上传。流程建议这样设计:

1
2
3
4
5
前端上传图片
  -> 后端校验格式和大小
  -> 保存到临时存储
  -> 上传或复制到 ComfyUI input 目录
  -> 把 ComfyUI 可识别的文件名写入 workflow

不要把用户本地路径传给 ComfyUI。浏览器里的文件路径对服务器没有意义,ComfyUI 也不应该读取任意服务器路径。

上传时至少检查:

  • 文件类型是否是允许的图片格式;
  • 文件大小是否超过限制;
  • 图片宽高是否过大;
  • 是否需要压缩或转成统一格式;
  • 文件名是否需要重新生成,避免特殊字符;
  • 临时文件是否会定期清理。

推荐使用内部文件 ID,而不是原始文件名:

1
upload_20260710_abcd.png

前端交互怎么做

前端不要让用户看见一堆技术状态。只需要几个可理解状态:

1
2
3
4
5
排队中
生成中
处理结果中
生成完成
生成失败

轮询可以这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
async function pollTask(taskId) {
  const res = await fetch(`/api/tasks/${taskId}`);
  const task = await res.json();

  if (task.status === "succeeded") {
    showResult(task.results);
    return;
  }

  if (task.status === "failed") {
    showError(task.error_message || "生成失败,请稍后重试");
    return;
  }

  setTimeout(() => pollTask(taskId), 2000);
}

第一版用轮询就够了。等任务量上来后,再考虑 WebSocket 或 Server-Sent Events。

后端提交 ComfyUI 的伪代码

后端核心逻辑大致是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
async function submitToComfy(task) {
  const workflow = loadWorkflowTemplate(task.workflow);
  const mapping = loadWorkflowMapping(task.workflow);

  applyInput(workflow, mapping.positive_prompt, task.prompt);
  applyInput(workflow, mapping.negative_prompt, task.negative_prompt);
  applyInput(workflow, mapping.seed, task.seed);

  if (task.input_image_name) {
    applyInput(workflow, mapping.input_image, task.input_image_name);
  }

  const res = await fetch("http://127.0.0.1:8188/prompt", {
    method: "POST",
    headers: {"Content-Type": "application/json"},
    body: JSON.stringify({
      prompt: workflow,
      client_id: task.task_id
    })
  });

  const data = await res.json();
  return data.prompt_id;
}

真实项目里还要加超时、重试、日志、异常捕获和任务状态更新。不要只写一个 fetch 就上线。

结果文件怎么存

ComfyUI 输出的图片通常在 output 目录里。网站上线时有两种做法:

方案一:直接从后端代理下载

后端读取 ComfyUI 输出,再通过网站接口返回。

优点是简单。缺点是后端流量压力会变大,文件清理也要自己处理。

方案二:上传到对象存储

后端拿到结果文件后,上传到 S3、R2、OSS、COS 或自建文件服务。

优点是更适合正式网站:

  • 前端访问稳定 URL;
  • 文件 CDN 加速;
  • 方便做过期清理;
  • 方便按用户和任务管理;
  • ComfyUI 机器可以只负责生成。

正式产品更推荐第二种。

为什么需要队列

ComfyUI 通常不适合被网站请求直接打满。用户一多,GPU 会排队,显存可能爆掉,任务也会互相影响。

建议后端加一个队列层:

1
2
3
4
5
6
7
POST /api/generate
  -> 写入数据库 status=queued
  -> 推入队列
  -> worker 取任务
  -> 提交 ComfyUI
  -> 轮询结果
  -> 更新数据库

队列可以先很简单:数据库轮询、Redis queue、BullMQ、Celery、Sidekiq 都可以。关键是不要让用户请求线程一直等 GPU。

错误处理要给用户能懂的信息

ComfyUI 的原始报错可能很长,也可能包含内部路径。不要直接返回给用户。后端可以把错误分成几类:

内部原因 用户提示
输入图片太大 图片尺寸过大,请压缩后重试
workflow 校验失败 当前模板暂不可用,请稍后重试
GPU 显存不足 当前生成任务繁忙,请稍后重试
任务超时 生成超时,请减少图片尺寸或稍后再试
模型缺失 当前模板维护中

内部日志保留详细错误,用户界面只给可理解、可行动的提示。

上线前要加哪些保护

把 ComfyUI 接进网站后,至少要做这些保护:

  • 限制图片大小和分辨率;
  • 限制 prompt 长度;
  • 限制单用户并发任务数;
  • 限制每个模板可选参数范围;
  • 给任务设置超时时间;
  • 失败任务可重试但不能无限重试;
  • 输出文件定期清理;
  • 内部 ComfyUI 地址不暴露公网;
  • 管理后台能禁用某个模板;
  • 记录任务耗时、失败率和 GPU 使用情况。

这些保护比“模型效果更好一点”更重要。网站上线后,稳定性和成本会很快变成主要问题。

RunningHub 和自建 ComfyUI 怎么选

如果你不想维护 GPU、模型、节点和队列,可以考虑 RunningHub 这类平台的 Workflow API。它更适合快速验证、模板复用和云端托管。

自建 ComfyUI 更适合:

  • 需要深度控制节点和模型;
  • 有自己的 GPU 机器;
  • 需要私有化部署;
  • 需要自定义节点;
  • 对成本和数据边界有明确要求。

RunningHub 更适合:

  • 团队不想维护环境;
  • 想快速上线视觉生成工具;
  • 工作流模板已经在平台上可用;
  • 希望 API、模型和运行环境由平台托管。

两种路线前端架构可以保持一致:前端调用你的网站后端,后端再决定调用自建 ComfyUI 还是云端 Workflow API。

一个推荐落地顺序

第一次接入网站,可以按这个顺序做:

  1. 在 ComfyUI 里跑通一个稳定工作流。
  2. 导出 API workflow JSON。
  3. 在本地脚本里跑通 /prompt/history/view
  4. 后端封装 POST /api/generate
  5. 加任务表和状态查询接口。
  6. 前端做表单、提交和轮询。
  7. 结果文件先本地保存,再迁移到对象存储。
  8. 加输入限制、并发限制和超时。
  9. 再接用户系统、计费、模板库和后台管理。

不要跳过第 3 步。如果本地脚本都不能稳定生成,网站接入只会把问题藏得更深。

FAQ

前端可以直接调用 ComfyUI API 吗?

不建议。前端直连会暴露 ComfyUI 地址、工作流结构和内部参数,也不好做权限、限流、文件管理和错误处理。正式网站应该通过自己的后端转发和管理任务。

网站需要 WebSocket 吗?

第一版不需要。用 task_id 轮询状态就够了。等任务量变大、进度展示要求更高时,再考虑 WebSocket 或 SSE。

workflow JSON 可以让用户上传吗?

不建议。用户上传任意 workflow 会带来安全、成本和稳定性问题。更稳的方式是后端维护模板白名单,用户只能填有限参数。

生成结果应该存在哪里?

开发阶段可以先放本地 output 目录。正式网站建议上传到对象存储或文件服务,再返回稳定 URL,方便 CDN、权限控制和过期清理。

一个网站能接多个 ComfyUI 工作流吗?

可以。建议每个工作流都有独立模板、参数映射、资源限制和错误处理。不要把所有工作流混在一个通用接口里乱传参数。

视频工作流和图片工作流接入一样吗?

思路相同,但视频任务更慢、文件更大、失败率更高。视频工作流更需要队列、超时、进度提示、对象存储和后台重试机制。

小结

ComfyUI 工作流 API 接入网站,重点不是把 /prompt 请求发出去,而是把生成过程产品化:前端收集用户输入,后端维护模板和任务状态,队列控制 GPU 压力,ComfyUI 执行工作流,结果文件进入可访问的存储,用户看到清晰状态和错误提示。

第一版先做一个模板、一个任务接口、一个状态接口和一个结果页。等这个闭环稳定后,再扩展多模板、用户系统、计费、对象存储、后台管理和云端 Workflow API。这样做出来的网站才不是一个脆弱的 ComfyUI 外壳,而是一个可维护的 AI 生成产品。

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计