从零搭建移动端数字人生成应用:React + Go + D‑ID 实战
本文记录一次从零到一的落地过程:在移动端界面中上传头像,选择"文本合成(TTS)"或"外部音频驱动",由后端调用 D‑ID 生成口播视频,前端轮询任务状态并展示结果。项目地址结构与关键代码已在文末附带定位,便于对照阅读与扩展。
背景与目标
- 背景:需要一个轻量、可移植的 Demo,用于在手机端快速体验"数字人生成"完整链路。
- 目标:
- 前端拥有友好的移动端交互(上传、脚本选择、进度反馈、预览)。
- 后端封装 D‑ID 接口并负责静态资源托管与安全校验。
- 具备开发与生产两套可复用的运行方式。
演示效果:


总体架构
- 前后端分离:前端
React + Vite,后端Go + Gin。 - 访问路径:前端以相对路径
'/api'发起请求,经 Vite 代理到本地后端。 - 资源托管:后端将上传的文件落盘至
public/uploads并静态暴露为GET /static/uploads/<name>。 - 生成流程:
上传头像 → 选择脚本 → 创建任务 → 轮询状态 → 展示视频。
架构关键点:
- 端口与代理:
frontend/vite.config.ts:6-14指定开发端口5100,并将/api代理到http://localhost:8088。 - CORS 与静态目录:
backend/cmd/server/main.go:80-97,110-112设置跨域与静态目录挂载。
技术选型与权衡
- 前端:
React 18、Vite 5、TypeScript、antd-mobile、axios。选择这些是为了快速搭移动端交互与良好开发体验。 - 后端:
Go 1.22、Gin、godotenv。强调简单、可读、易部署;godotenv便于本地加载.env。 - 第三方:
D‑ID作为视频生成服务,接口简单,但需要严格的https资源输入与鉴权策略。
关键数据流与接口设计
- 上传图片:
POST /api/upload(backend/cmd/server/main.go:132-152)multipart/form-data字段file。- 保存到
public/uploads,返回拼接的url(受PUBLIC_BASE_URL影响)。
- 创建任务:
POST /api/talks(backend/cmd/server/main.go:153-227)- 入参包含
imageUrl、text、voice.lang、scriptType等。 - 重要校验:
imageUrl/audioUrl必须是公网https且可访问(后端会主动探测)。
- 入参包含
- 查询任务:
GET /api/talks/:id(backend/cmd/server/main.go:228-241)- 如配置了
DID_API_KEY,则直接透传 D‑ID 的原始返回(GetTalkRaw)。
- 如配置了
- 列表任务:
GET /api/talks(backend/cmd/server/main.go:242-249)。 - 健康检查:
GET /api/health(backend/cmd/server/main.go:113-119)。
鉴权策略与安全细节
- D‑ID 鉴权头:
- Basic:当
DID_API_KEY包含冒号时,编码为Basic <base64(user:pass)>。 - Bearer:否则为
Bearer <key>。 - 生成逻辑在
backend/internal/service/did.go:49-55。
- Basic:当
- 安全建议:
- 生产环境不应记录
Authorization明文(当前代码会log.Println("Authorization:", ah),见backend/internal/service/did.go:63;建议关闭)。 GET /api/did/debug仅用于开发验证鉴权类型(backend/cmd/server/main.go:120-131),生产建议移除或加权限控制。- 对上传文件建议增加类型校验与大小限制,避免存储滥用。
- 生产环境不应记录
前端实现要点
- 请求基址:
frontend/src/services/http.ts:3-6使用'/api'相对路径,利于网关统一代理。 - 任务创建与轮询:
- 创建:
createTalk(frontend/src/services/did.ts:6-9)。 - 轮询:
Home.tsx中useEffect每 2s 查询(frontend/src/pages/mobile/Home.tsx:92-106)。
- 创建:
- 脚本模式:
text:用户输入文案,后端走 TTS Provider(可传provider、voiceId)。audio:用户输入或采用默认音频 URL,需为公网https(默认见frontend/src/pages/mobile/Home.tsx:20)。
交互细节:
- 创建前校验最小文案长度与音频 URL 的
https前缀(frontend/src/pages/mobile/Home.tsx:42-66)。 - 超时/异常反馈通过
Toast统一处理(frontend/src/pages/mobile/Home.tsx:67-90)。
后端实现要点
- 资源拼接:若
PUBLIC_BASE_URL未设置,则退化为http://<请求Host>(backend/cmd/server/main.go:145-151)。 - D‑ID 客户端:
- 创建任务:
CreateTalkWithOpts(backend/internal/service/did.go:71-110)。 - 当
scriptType='audio'且未提供audioUrl时,优先读取AUDIO_URL_DEFAULT,否则使用内置示例(backend/internal/service/did.go:84-94,默认链接见backend/internal/service/did.go:91)。 - 原始查询:
GetTalkRaw(backend/internal/service/did.go:136-147)。
- 创建任务:
- 本地回退:当不满足公网
https条件或未配置DID_API_KEY时,采用本地模拟完成(返回示例视频,backend/cmd/server/main.go:216-224)。
运行与部署
- 开发:
- 后端:
cd backend && go run .\cmd\server\main.go。 - 前端:
cd frontend && npm install && npm run dev;访问http://localhost:5100。 - 健康检查:
GET http://localhost:8088/api/health。
- 后端:
- 生产:
- 后端构建:
cd backend && go build -o server .\cmd\server\main.go;运行./server(Windows 为server.exe)。 - 前端构建:
cd frontend && npm run build;部署frontend/dist到静态服务器。 - 代理:使用网关将
/api反向代理到后端,并设置后端CORS_ORIGINS为前端域名。
- 后端构建:
- 隧道(可选):
cloudflared tunnel --url http://localhost:8088。- 将生成的
https://xxxxx.trycloudflare.com赋值给PUBLIC_BASE_URL,用于上传文件的公网可达地址拼接与资源校验。
常见问题与排错
- 图片不可访问/非 HTTPS:后端会返回错误,参考
backend/cmd/server/main.go:170-181。 - 音频不可访问/非 HTTPS:参考
backend/cmd/server/main.go:185-201。 - 未设置
DID_API_KEY:将走本地模拟路径(便于演示),参考backend/cmd/server/main.go:216-224。 - 授权头泄漏:请移除
did.go中对Authorization的日志记录,避免泄漏敏感信息(backend/internal/service/did.go:63)。
性能与扩展建议
- 任务存储:当前为内存存储(
Store结构体,backend/cmd/server/main.go:52-78);生产建议改为持久化(数据库)并加入任务队列与重试机制。 - Webhook:
POST /api/webhook/did为占位(backend/cmd/server/main.go:250-254);可扩展为落库、缓存、通知。 - 上传安全:增加类型白名单、大小限制、病毒扫描;配合 CDN 进行加速与鉴权。
- 观感优化:在前端增加进度提示、错误细节弹窗、视频封面裁剪等。
环境变量与配置
- 示例文件:
backend/.env.example:1-4 - 关键变量:
DID_API_KEY:D‑ID 鉴权 Key;决定使用 Basic 或 Bearer(backend/cmd/server/main.go:120-131)。PUBLIC_BASE_URL:上传文件返回 URL 的前缀,需为公网https。PORT:后端监听端口,默认8088。CORS_ORIGINS:允许的前端来源,默认http://localhost:5100。AUDIO_URL_DEFAULT:音频模式下未提供audioUrl时的默认地址(backend/internal/service/did.go:84-94)。
代码定位索引
- 路由与接口:
backend/cmd/server/main.go:112-256 - D‑ID 客户端:
backend/internal/service/did.go:49-110,136-147 - 前端代理:
frontend/vite.config.ts:6-14 - axios 基址:
frontend/src/services/http.ts:3-6 - 页面流程:
frontend/src/pages/mobile/Home.tsx:37-106
总结一下哈
本文演示了一条清晰的落地路径:将第三方生成服务以后端为中心进行封装与治理,在前端提供面向移动端的简洁交互,并通过严格的 HTTPS 校验与隧道/网关方案确保资源可达与安全。后续可以在任务存储、回调处理、安全加固与观感优化方面继续演进,使之成为更稳健的生产级解决方案。
项目暂还没整理好准备开源,敬请等待!
