展开本页目录
开始前准备什么
- 明确做文生图还是参考图编辑;编辑上传需要完整解码、格式和尺寸检查。
- 服务端配置 tokenba_API_KEY,禁止 NEXT_PUBLIC_ 前缀和客户端 props 传 Key。
- 准备用户认证、每用户调用限额、持久任务表与可写存储,提前测试存储失败分支。
- 核对 VPS 或平台的上传、执行时长和临时磁盘限制,不能只依赖本地开发成功。
- 上传校验
- 创建业务任务
- worker 提交
- 保存原图
- 轮询与验收
本地业务接口设计样例,包含重复提交与未知状态;没有实现认证、队列或图片供应商任务接口。
前端、主站服务端和图片供应商,各做一件事
主站代码可以实现自己的业务层,无须修改 api.apizt.com 的 VPS。采用队列时,任务状态保存于数据库,worker 崩溃后应保留 submitting / unknown,先查证再重提;不要在 Route Handler 中启动不被托管的 Promise 后直接返回 202。
| 层 | 职责 | 保留的数据 |
|---|---|---|
| 浏览器 | 选素材、填提示词、展示已有任务 | 业务 job_id 和用户自己的结果 |
| Route Handler | 校验身份与输入,创建或返回已有任务 | user_id、request_key、job_id |
| 持久 worker | 领取任务,调用一次供应商并保存结果 | 请求标识、状态、错误和 usage |
| 数据库与对象存储 | 持久任务状态和完整原图 | 文件对象键、真实宽高、哈希与验收结果 |
- 层
- 浏览器
- 职责
- 选素材、填提示词、展示已有任务
- 保留的数据
- 业务 job_id 和用户自己的结果
- 层
- Route Handler
- 职责
- 校验身份与输入,创建或返回已有任务
- 保留的数据
- user_id、request_key、job_id
- 层
- 持久 worker
- 职责
- 领取任务,调用一次供应商并保存结果
- 保留的数据
- 请求标识、状态、错误和 usage
- 层
- 数据库与对象存储
- 职责
- 持久任务状态和完整原图
- 保留的数据
- 文件对象键、真实宽高、哈希与验收结果
Route Handler 先验输入,再创建任务
这段展示控制顺序,authenticate 与 imageJobs 是需要实现的业务依赖,不能直接粘贴运行。createOrGet 应以 user_id + request_key 建唯一约束;相同 Key 配不同内容应拒绝。该约束只阻止应用里的重复点击,不证明供应商提供幂等性。
app/api/image-jobs/route.ts:接入位置片段,需实现自己的认证和任务库
import { authenticate, imageJobs } from "@/lib/image-jobs";
export async function POST(request: Request) {
const user = await authenticate(request);
if (!user) return Response.json({ error: "unauthorized" }, { status: 401 });
const body: unknown = await request.json();
const parsed = imageJobs.validateCreate(body);
if (!parsed.ok) return Response.json({ error: parsed.error }, { status: 400 });
const job = await imageJobs.createOrGet(user.id, parsed.value);
return Response.json({ job_id: job.id, status: job.status }, { status: 202 });
}编辑上传不能只检查文件名
- 由反向代理和应用限制请求体字节数;读取 FormData 前也应有入口限制,避免先把超大文件载入内存再拒绝。
- 上传文件用受控随机对象键保存,不用用户文件名拼路径;完整解码实际格式,并限制像素数量。
- image 与 mask 分别校验,再检查画布对应和 alpha;客户端的 accept 和 MIME 声明只是提示。
- 参考图只让当前任务的拥有者访问;需要临时 URL 时配置短有效期,并确认供应商能在有效期内读取。
轮询自己的任务,别重复触发供应商
网络断开、浏览器刷新和 worker 超时都不应直接触发第二次生成。结果保存失败时先保留已收到的响应和暂存文件,重试存储;无法确认提交结果时标 unknown,不自动变回 queued。
| 业务接口 | 返回 | 关键限制 |
|---|---|---|
| POST /api/image-jobs | job_id 与 queued 或现有状态 | 认证、配额、唯一请求键 |
| GET /api/image-jobs/:id | 状态、可展示错误、结果元数据 | 校验任务归属;不执行新生成 |
| GET /api/image-jobs/:id/result | 自己保存的完整结果 | 权限、Content-Type 和下载范围 |
| 验收记录 | accepted / rejected 及理由 | 供应商成功与内容通过分别记录 |
- 业务接口
- POST /api/image-jobs
- 返回
- job_id 与 queued 或现有状态
- 关键限制
- 认证、配额、唯一请求键
- 业务接口
- GET /api/image-jobs/:id
- 返回
- 状态、可展示错误、结果元数据
- 关键限制
- 校验任务归属;不执行新生成
- 业务接口
- GET /api/image-jobs/:id/result
- 返回
- 自己保存的完整结果
- 关键限制
- 权限、Content-Type 和下载范围
- 业务接口
- 验收记录
- 返回
- accepted / rejected 及理由
- 关键限制
- 供应商成功与内容通过分别记录
上线前在真实部署环境检查四种失败
本页提供应用设计和接入位置,未提供可部署的完整图片 SaaS,也未验证你的平台时限。真实 Key 接入前,用固定本地图片和模拟错误完成这四项;随后参考已有 Node SDK,用自己的通道提交一次最小请求。
| 场景 | 应有行为 | 验证方式 |
|---|---|---|
| 用户双击提交 | 返回同一个业务任务 | 两次相同 request_key,供应商只提交一次 |
| 未登录或他人任务 ID | 拒绝操作或读取 | 用两个用户分别尝试访问 |
| 对象存储不可写 | 提交前失败,或保存失败保留原响应 | 关闭测试目录权限,核对调用次数 |
| 供应商慢响应或断连 | 保留任务并显示状态待确认 | 模拟超时,刷新后不重复创建调用 |
- 场景
- 用户双击提交
- 应有行为
- 返回同一个业务任务
- 验证方式
- 两次相同 request_key,供应商只提交一次
- 场景
- 未登录或他人任务 ID
- 应有行为
- 拒绝操作或读取
- 验证方式
- 用两个用户分别尝试访问
- 场景
- 对象存储不可写
- 应有行为
- 提交前失败,或保存失败保留原响应
- 验证方式
- 关闭测试目录权限,核对调用次数
- 场景
- 供应商慢响应或断连
- 应有行为
- 保留任务并显示状态待确认
- 验证方式
- 模拟超时,刷新后不重复创建调用
怎样确认已经完成
- 前端 JS、响应和日志中无 API Key。
- 重复点击与页面刷新不创建第二次供应商调用。
- 上传入口、实际解码和存储权限均已验证。
- 超时状态可以追溯,保存失败不会丢掉已经收到的图片。
- 部署环境已检查执行时长、任务持久化和结果权限。
常见问题
可以直接从浏览器请求 apizt 吗?
公开产品使用固定服务 Key 时应经过自己的服务端,避免 Key 暴露。若做 BYOK 本地工具,也应明确用户 Key 的保管与传输方式,不能混用平台服务 Key。
Next.js 返回 202 就会后台生成吗?
不会。202 只是你的 HTTP 响应,需要真正的持久队列和 worker 执行任务;普通未等待 Promise 不保证部署环境继续运行。