React Native向Heroku API发送图片持续返回400错误如何解决
React Native 拍照上传接口返回400错误修复
问题背景
正在开发一款支持拍摄照片、返回图片内检测物体列表的Expo React Native应用,已将API地址替换为insert api url here做脱敏处理,实际使用的API地址已在Postman中验证可正常运行。
现有逻辑为:拍摄照片后将图片本地URI存入state,通过axios将该URI作为参数提交给接口,调用时持续返回400错误:
[AxiosError: Request failed with status code 400]
原有项目完整代码
import React, { useState, useEffect, useRef } from "react"; import { Text, View, StyleSheet, TouchableOpacity, Image } from "react-native"; import Constants from "expo-constants"; import { Camera, CameraType } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; import { MaterialIcons } from "@expo/vector-icons"; import Button from "./src/components/Button"; import axios from "axios"; export default function App() { const [hasCameraPermission, setHasCameraPermission] = useState(null); const [image, setImage] = useState(null); const [type, setType] = useState(Camera.Constants.Type.back); const [flash, setFlash] = useState(Camera.Constants.FlashMode.off); const cameraRef = useRef(null); useEffect(() => { (async () => { MediaLibrary.requestPermissionsAsync(); const cameraStatus = await Camera.requestCameraPermissionsAsync(); setHasCameraPermission(cameraStatus.status === "granted"); })(); }, []); const takePicture = async () => { if (cameraRef) { try { const data = await cameraRef.current.takePictureAsync(); console.log(data); setImage(data.uri); } catch (error) { console.log(error); } } }; const savePicture = async () => { if (image) { try { const asset = await MediaLibrary.createAssetAsync(image); axios .post("insert api url here", { Imagee: image, }) .then((response) => { console.log(response); }) .catch((error) => { console.log(error); }); alert("done"); setImage(null); console.log("saved successfully"); } catch (error) { console.log(error); } } }; if (hasCameraPermission === false) { return <Text>No access to camera</Text>; } return ( <View style={styles.container}> {!image ? ( <Camera style={styles.camera} type={type} ref={cameraRef} flashMode={flash} > <View style={{ flexDirection: "row", justifyContent: "space-between", paddingHorizontal: 30, }} > <Button title="" icon="retweet" onPress={() => { setType( type === CameraType.back ? CameraType.front : CameraType.back ); }} /> <Button onPress={() => setFlash( flash === Camera.Constants.FlashMode.off ? Camera.Constants.FlashMode.on : Camera.Constants.FlashMode.off ) } icon="flash" color={flash === Camera.Constants.FlashMode.off ? "gray" : "#fff"} /> </View> </Camera> ) : ( <Image source={{ uri: image }} style={styles.camera} /> )} <View style={styles.controls}> {image ? ( <View style={{ flexDirection: "row", justifyContent: "space-between", paddingHorizontal: 50, }} > <Button title="Re-take" onPress={() => setImage(null)} icon="retweet" /> <Button type="submit" title="Save" onPress={savePicture} icon="check" /> </View> ) : ( <Button title="Take a picture" onPress={takePicture} icon="camera" /> )} </View> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", paddingTop: Constants.statusBarHeight, backgroundColor: "#000", padding: 8, }, controls: { flex: 0.5, }, button: { height: 40, borderRadius: 6, flexDirection: "row", alignItems: "center", justifyContent: "center", }, text: { fontWeight: "bold", fontSize: 16, color: "#E9730F", marginLeft: 10, }, camera: { flex: 5, borderRadius: 20, }, topControls: { flex: 1, }, });
核心问题原因
400错误本质是请求格式不符合接口要求:
- 代码中提交的
image是Expo相机返回的本地文件路径字符串(格式类似file:///本地存储路径/xxx.jpg),不是接口需要的图片文件实体 - Postman调用成功是因为Postman上传时默认使用
multipart/form-data格式传输真实图片二进制内容,和现有代码发送JSON传本地路径的请求格式完全不匹配 - 现有代码还存在逻辑缺陷:没有等待接口请求完成就弹出成功提示、清空图片状态,请求失败也会误报成功;同时混用async/await和.then/.catch写法,异常捕获不完整
修复步骤
- 安装文件读取依赖,用于获取本地图片信息:
expo install expo-file-system
- 在文件头部引入依赖:
import * as FileSystem from 'expo-file-system';
- 替换原有
savePicture方法,使用FormData格式上传真实图片文件:
const savePicture = async () => { if (!image) return; try { // 保存图片到系统相册 await MediaLibrary.createAssetAsync(image); // 构造FormData上传格式 const formData = new FormData(); formData.append('Imagee', { uri: image, name: `photo_${Date.now()}.jpg`, type: 'image/jpeg', }); // 发起请求,指定正确的Content-Type const response = await axios.post( "insert api url here", formData, { headers: { 'Content-Type': 'multipart/form-data', }, } ); console.log('接口返回:', response.data); alert("上传成功"); setImage(null); } catch (error) { console.log('请求错误:', error); alert(`上传失败:${error.response?.data?.message || error.message}`); } };
校验点
- 确认FormData中append的字段名
Imagee和接口要求的字段名完全一致,注意大小写 - 如果Postman中使用的是其他请求格式(比如base64传参),对应调整传参格式即可,绝大多数图片识别接口默认接收multipart/form-data格式
- 安卓端打包正式版时,需要在app.json中声明文件读取、相机权限,否则会出现本地文件访问失败问题
内容的提问来源于stack exchange,提问作者bam mazino
相关产品推荐
相关产品推荐

