React Native调用Heroku端FastAPI文件上传接口报400错误解决方法
问题描述
实现文件上传功能,将文件作为入参传递给FastAPI后端处理时,Swagger内接口测试可正常运行,但从React Native手机端调用部署在Heroku上的对应接口始终失败,返回400 Bad Request错误。首次接触API开发,无法确认请求体参数格式、接口调用方式是否正确,需要明确接收文件类型入参的API的正确调用方法。
相关参考截图
现有代码
React Native端代码
const getImg = async imageUrl => { const response = await fetch(imageUrl); const imageBlob = await response.blob(); const reader = new FileReader(); reader.readAsDataURL(imageBlob); reader.onloadend = () => { const base64data = reader.result; setImgUrl(base64data); }; }; const chooseFromLibrary = () => { ImagePicker.openPicker({ width: 300, height: 400, cropping: false, }).then(image => { console.log(image); console.log(image['mime']); getImg(image.path); hi(); }); }; const hi = async () => { let response = await fetch( 'https://receipt-ocr-heroku.herokuapp.com/receiptOcr', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'multipart/form-data', }, body: JSON.stringify({ file: imgUrl, }), }, ); console.log(response); let result = await response.json(); console.log(result); };
FastAPI端代码
@app.post("/receiptOcr") async def main(file: UploadFile = File(...)): try: image = await file.read() # image = cv2.imread(image) nparr = np.fromstring(image, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
错误原因
现有代码存在3个直接导致400错误的问题:
- 异步时序错误:
getImg是异步读取文件的操作,调用后立刻执行hi()发起请求时,base64格式的imgUrl还未完成赋值,实际传参为空。 - 请求格式错误:
multipart/form-data类型请求不能用JSON.stringify包裹请求体,手动固定设置Content-Type: multipart/form-data会导致fetch无法自动生成携带随机boundary分隔符的合法请求头,FastAPI无法解析请求体。 - 参数格式错误:FastAPI的
UploadFile类型要求接收标准multipart格式的文件流,而非base64字符串,将文件转base64后塞入JSON结构完全不符合接口入参要求。
修复方案
- 删除多余的base64转换逻辑,直接使用标准
FormData对象构造上传参数,不需要额外转码。 - 移除手动设置的
Content-Type请求头,由fetch自动生成携带正确boundary的请求头。 - 修正异步时序逻辑,选完图片后直接构造参数发起请求,避免异步赋值导致的参数为空问题。
修正后的React Native上传代码如下:
const chooseFromLibrary = () => { ImagePicker.openPicker({ width: 300, height: 400, cropping: false, }).then(async (image) => { // 构造multipart上传表单 const formData = new FormData(); // 字段名必须和FastAPI接口的参数名`file`完全一致 formData.append('file', { uri: image.path, type: image.mime, name: image.filename || 'receipt.jpg' }); try { const response = await fetch( 'https://receipt-ocr-heroku.herokuapp.com/receiptOcr', { method: 'POST', headers: { Accept: 'application/json' // 不要手动设置Content-Type,否则会丢失boundary标识 }, body: formData } ); const result = await response.json(); console.log('接口返回结果', result); } catch (err) { console.log('上传请求异常', err); } }); };
- 注意事项:FastAPI端
UploadFile参数默认匹配和参数名完全一致的表单字段,这里后端参数名为file,FormData追加字段的key必须是file,大小写完全匹配才能被正确识别。Heroku免费版对单请求体大小限制为30MB,上传时注意文件大小不要超限。 - 额外提示:FastAPI端使用的
np.fromstring方法在numpy新版本中已被弃用,建议替换为np.frombuffer避免后续运行警告。
内容的提问来源于stack exchange,提问作者random
相关产品推荐
相关产品推荐

