使用 Gradio 后端搭配任意自定义前端

几周前,我们介绍了通过 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 流式传输实现实时更新、批量处理,以及构建共享状态的多页面应用的模式。

我们会在后续文章中深入介绍,敬请期待。

推荐阅读

原文:Any Custom Frontend with Gradio’s Backend。作者/来源:yuvraj sharma(ysharma)、Abubakar Abid(abidlabs),2026年4月1日。本文为依据用户明确授权制作的中文翻译;未确认单独公开内容许可证,不将软件许可证视为正文或图片许可。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容