几周前,我们介绍了通过 gr.HTML一次生成完整 Web 应用:使用自定义 HTML、CSS 和 JavaScript,完全在 Gradio 内部构建丰富、交互式的前端。这打开了许多可能性。但如果还不够呢?
如果你希望完全使用自己的前端框架,例如 React、Svelte,甚至纯 HTML/JS,同时仍然享有 Gradio 的排队系统、API 基础设施、MCP 支持,以及 Spaces 上的 ZeroGPU,该怎么办?
这正是 gradio.Server 解决的问题。它改变了 Gradio 和 Hugging Face Spaces 能够实现的事情。
我们想构建什么
Text Behind Image:一个编辑器,用户上传照片后,机器学习模型移除背景,再将带样式的文字放在前景主体与背景之间。文字看起来就位于照片里的人或物体后面。
这需要:
- 支持拖放的画布,按图层渲染(背景 → 文字 → 前景)
- 丰富的控制面板,提供字体大小、字重、字间距、颜色、不透明度、描边、阴影、3D 挤出、透视变换等滑块
- 一个后端机器学习端点,运行背景移除模型并返回透明 PNG
- 在客户端导出 PNG
无法用 Gradio 组件表达这样的界面。这是一个完整的 Web 应用。但我们仍然希望使用 Gradio 强大的后端能力:排队、并发管理、ZeroGPU 支持,以及无需操心基础设施的 HF Spaces 托管。
引入 gradio.Server
gradio.Server 扩展了 FastAPI。它保留 FastAPI 的全部能力,包括自定义路由、中间件、文件上传和任意响应类型,同时加入 Gradio 的 API 引擎:排队、SSE 流式传输、并发控制,以及 gradio_client 兼容性。
下面是 Text Behind Image 的完整后端:
import os
import torch
from PIL import Image
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse
import spaces
torch.set_float32_matmul_precision("high")
birefnet = AutoModelForImageSegmentation.from_pretrained(
"ZhengPeng7/BiRefNet", trust_remote_code=True
)
birefnet.to("cuda")
birefnet.float()
transform_image = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
app = Server()
@spaces.GPU
def segment(image: Image.Image) -> Image.Image:
"""Run BiRefNet segmentation to produce a transparency mask."""
image_size = image.size
input_images = transform_image(image).unsqueeze(0).to("cuda")
with torch.no_grad():
preds = birefnet(input_images)[-1].sigmoid().cpu()
pred = preds[0].squeeze()
mask = transforms.ToPILImage()(pred).resize(image_size)
image.putalpha(mask)
return image
@app.api(name="remove_background")
def remove_background(image_path: FileData) -> FileData:
"""Remove background from an image. Returns transparent PNG."""
im = Image.open(image_path["path"]).convert("RGB")
result = segment(im)
out_path = image_path["path"].rsplit(".", 1)[0] + ".png"
result.save(out_path)
return FileData(path=out_path)
@app.get("/", response_class=HTMLResponse)
async def homepage():
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
with open(html_path, "r", encoding="utf-8") as f:
return f.read()
app.launch(show_error=True)
就这些,大约 50 行 Python。模型在启动时加载,@spaces.GPU 负责 ZeroGPU 分配,gradio.Server 管理排队和并发。下面拆解其中的机制。
为什么使用 @app.api(),而不是普通 FastAPI 路由?
在普通 FastAPI 应用中,你会为背景移除定义一个 @app.post() 路由。它能够工作,直到两个用户同时访问。没有并发管理时,两个请求会争用 GPU,导致应用崩溃或返回错误结果。
@app.api() 解决了这个问题。它使用 Gradio 排队引擎包装函数:请求按序执行,并发受到控制;在 ZeroGPU Spaces 上,GPU 分配由 @spaces.GPU 自动处理。此外,任何 @app.api() 端点都可以通过 gradio_client 调用,其他应用或脚本也能以编程方式使用你的 Space:
from gradio_client import Client, handle_file
client = Client("ysharma/text-behind-image")
result = client.predict(
image_path=handle_file("photo.jpg"),
api_name="/remove_background"
)
与此同时,@app.get("/") 是一个提供 HTML 页面的标准 FastAPI 路由。两者可以自然共存,因为 Server 本身就是 FastAPI 应用。
前端:纯 HTML/CSS/JS
本例的 index.html 是一个独立的、约 1300 行的 Web 应用。无需 React、构建步骤或打包器,只使用原生 HTML,包含:
- 三层画布:背景图 → 文字层 → 前景(透明 PNG),使用 CSS
z-index叠放 - 使用指针事件拖放定位文字
- 包含 20 多个参数的控制面板:字体家族(25 多种字体)、大小、字重、间距、颜色、不透明度、背景填充、描边、阴影、3D 挤出深度及角度、旋转、倾斜,以及完整的 CSS 透视变换
- 使用
<canvas>合成,在客户端导出 PNG
前端使用 Gradio JS Client 与后端通信:
import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const client = await Client.connect(window.location.origin);
const result = await client.predict("/remove_background", {
image_path: handle_file(file),
});
foregroundLayer.src = result.data[0].url; // transparent PNG
关键就在这里:使用 Gradio JS 客户端,而不是直接调用 fetch(),前端就会经过 Gradio 的队列。这意味着并发受到管理,GPU 请求不会冲突,甚至还可以向用户展示队列位置或进度。其余工作,包括文字渲染、图层合成和导出,都在浏览器中完成。
这带来了哪些能力
下面是 gradio.Server 出现之前无法实现的事情:
| 以前 | 现在 |
|---|---|
| 自定义界面意味着完全离开 Gradio | 自定义界面结合 Gradio 后端引擎 |
| 无法从 Gradio 应用提供静态 HTML | @app.get("/") 可以提供任意内容 |
gradio_client 只能用于 Gradio 组件应用 |
@app.api() 端点兼容客户端 |
| 必须在 Gradio 基础设施与设计自由之间选择 | 两者兼得 |
借助 gradio.Server,Gradio 同时也是后端框架:需要它的界面系统时就使用,不需要时就带上自己的前端。
如果想要 Gradio 的界面,可以使用 gr.Blocks、gr.Interface 或 gr.ChatInterface。如果想要自己的界面,就使用 gradio.Server,搭配任意喜欢的前端。无论哪种方式,都能得到 Spaces 托管、API 排队、gradio_client 访问,以及完整的 HF 生态系统等能力。
试试看
应用已在 Spaces 上线:ysharma/text-behind-image
上传任意一张主体清晰的照片,开始在主体后方叠放文字。试试 3D 挤出、透视倾斜和描边效果,它们结合起来很不错。
接下来
本文介绍了核心思路:gradio.Server 允许将任意前端与 Gradio 后端配对。还有更多值得探索,包括通过 @app.mcp.tool() 注册 MCP 工具、使用 SSE 流式传输实现实时更新、批量处理,以及构建共享状态的多页面应用的模式。
我们会在后续文章中深入介绍,敬请期待。
推荐阅读
- 文档:gradio.Server
- 指南:服务器模式











暂无评论内容