开发TripoSR的FastAPI接口时遭遇JSON序列化错误求助
问题分析与解决方案
问题根源
- 请求方式错误:原接口使用
GET请求,无法高效传输图片文件,应改为POST。 - 参数传递错误:调用端将图片字节流直接放入JSON payload,JSON不支持序列化bytes类型,需改用
multipart/form-data格式上传文件。 - 接口参数类型不匹配:FastAPI接口未正确定义文件上传参数,直接声明
PIL.Image.Image无法自动解析上传的文件。 - 返回值格式问题:原接口返回
{rv}写法错误,若generate返回文件字节或复杂结构,需处理为前端可接收的格式(如base64编码)。
FastAPI接口修正代码
from fastapi import FastAPI, UploadFile, File, Form from gradio_app import preprocess, generate from PIL import Image import io import base64 from typing import List, Optional app = FastAPI() @app.post("/tripo-api") def generator( input_image: UploadFile = File(...), do_remove_background: bool = Form(...), foreground_ratio: float = Form(...), mc_resolution: int = Form(...), formats: Optional[List[str]] = Form(default=["obj", "glb"]) ): """ 生成3D模型的API接口,调用preprocess和generate函数 """ # 读取上传的图片并转为PIL.Image img_bytes = input_image.file.read() pil_img = Image.open(io.BytesIO(img_bytes)).convert("RGB") # 调用预处理函数 output_prepr = preprocess( input_image=pil_img, do_remove_background=do_remove_background, foreground_ratio=foreground_ratio ) # 调用生成函数 rv = generate( output_prepr, mc_resolution=mc_resolution, formats=formats ) # 将生成的文件转为base64,方便前端接收 response_data = {} # 假设generate返回{格式: 文件字节}的字典,需根据实际返回结构调整 for fmt, file_bytes in rv.items(): response_data[fmt] = base64.b64encode(file_bytes).decode("utf-8") return response_data
调用端修正代码
import requests import base64 # API endpoint url = "http://localhost:8000/tripo-api" # 示例参数 input_image_path = "examples/captured.jpeg" do_remove_background = True foreground_ratio = 0.8 mc_resolution = 512 formats = ["obj", "glb"] # 准备请求数据:文件用files,其他参数用data files = {"input_image": open(input_image_path, "rb")} data = { "do_remove_background": do_remove_background, "foreground_ratio": foreground_ratio, "mc_resolution": mc_resolution, "formats": formats } # 发送POST请求 response = requests.post(url, files=files, data=data) # 处理响应 if response.status_code == 200: print("API请求成功!") result = response.json() # 示例:将base64转回文件 for fmt, b64_str in result.items(): with open(f"output.{fmt}", "wb") as f: f.write(base64.b64decode(b64_str)) else: print(f"API请求失败,状态码: {response.status_code}") print(f"错误信息: {response.text}")
前端适配说明
前端需通过FormData对象上传文件和参数:
- 创建包含文件输入框和参数控件的表单
- 收集数据并发送请求:
const formData = new FormData(); formData.append("input_image", document.getElementById("image-input").files[0]); formData.append("do_remove_background", true); formData.append("foreground_ratio", 0.8); formData.append("mc_resolution", 512); formData.append("formats", "obj"); formData.append("formats", "glb"); fetch("http://localhost:8000/tripo-api", { method: "POST", body: formData }) .then(response => response.json()) .then(data => { // 处理返回的base64数据,下载模型文件 for (const fmt in data) { const blob = b64toBlob(data[fmt], getMimeType(fmt)); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `model.${fmt}`; a.click(); URL.revokeObjectURL(url); } }) .catch(error => console.error("请求失败:", error)); // base64转Blob辅助函数 function b64toBlob(b64Data, contentType = "", sliceSize = 512) { const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } return new Blob(byteArrays, { type: contentType }); } // 获取格式对应MIME类型 function getMimeType(fmt) { const mimeMap = { "obj": "text/plain", "glb": "model/gltf-binary" }; return mimeMap[fmt] || "application/octet-stream"; }
内容的提问来源于stack exchange,提问作者zlElo
相关产品推荐
相关产品推荐

