React Native+Express上传图片返回200但文件未保存问题排查
问题排查与修复
你收到200响应但文件未保存,是前后端代码共存在多处语法、配置错误导致的,逐个修复即可:
后端(Multer端)错误点
- 配置项拼写错误:磁盘存储配置的
destintion为拼写错误,正确字段名是destination,Multer无法识别错误字段,不会执行文件写入逻辑 - 文件名生成逻辑错误:
callback(null, Date.now() = path.extname(file.originalname))中错误使用赋值运算符=拼接文件后缀,应该用字符串拼接符+;另外使用path.extname前需要先引入Node内置的path模块 - 路由方法不匹配:上传是POST请求,不应该用
app.use注册接口,要改用app.post - 路径兼容问题:存储路径
avatars写相对路径容易出现路径匹配错误,建议结合__dirname写绝对路径,且Multer不会自动创建不存在的目录,需要提前手动创建avatars文件夹,或启动时通过fs模块自动创建。
修复后的后端参考代码:
const express = require('express'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const app = express(); // 自动创建avatars存储目录 const avatarDir = path.join(__dirname, 'avatars'); if (!fs.existsSync(avatarDir)) { fs.mkdirSync(avatarDir, { recursive: true }); } const storage = multer.diskStorage({ destination: (req, file, callback) => { callback(null, avatarDir) }, filename: (req, file, callback) => { callback(null, Date.now() + path.extname(file.originalname)); } }); const upload = multer({storage: storage}); // 改用app.post接收POST上传请求 app.post("/uploadAvatar", upload.single("avatar"), (req, res) => { res.status(200).json("Image enregistrée !") }) app.listen(8000, () => console.log('服务启动在8000端口'))
前端(React Native端)错误点
- 缺少取消选择判断:调用DocumentPicker后要先判断用户是否取消了选择,否则用户点取消后拿不到uri会直接抛错
- FormData传参冗余:append文件对象时不需要额外加
file: result.file字段,只需要传uri、type、name三个属性即可 - 请求头配置错误:不要手动设置
Content-Type: multipart/form-data,multipart/form-data请求需要自动携带随机生成的boundary分隔符,手动写死请求头会导致boundary丢失,后端无法解析到上传的文件 - 冗余配置:自定义的
transformRequest完全多余,Axios识别到FormData类型的请求体时会自动做格式转换,直接删掉即可 - 版本兼容问题:Expo SDK 48以上版本的DocumentPicker返回结构调整为
{ canceled: boolean, assets: [...] }格式,要从assets数组里取文件属性,直接取顶层的uri、fileName会拿到undefined。
修复后的前端参考代码:
import * as DocumentPicker from 'expo-document-picker'; import axios from 'axios'; const pickPicture = async () => { let result = await DocumentPicker.getDocumentAsync({ type: 'image/*' // 可限制仅选择图片文件 }); // 用户取消选择直接终止流程 if (result.canceled) { return; } // 兼容新旧版本DocumentPicker返回结构 const fileAsset = result.assets ? result.assets[0] : result; console.log(fileAsset); const data = new FormData(); data.append("avatar", { uri: fileAsset.uri, type: fileAsset.mimeType || fileAsset.type, name: fileAsset.name || fileAsset.fileName, }); axios .post( `http://localhost:8000/uploadAvatar`, data, { withCredentials: true, // 不要手动设置Content-Type,让Axios自动生成带boundary的合法请求头 } ) .catch(function (error) { console.log('上传失败', error); }) .then(function (res) { console.log('上传成功', res); }); }
补充:如果用安卓本地模拟器调试,前端请求地址不要写
localhost,要换成10.0.2.2才能访问到电脑本地启动的Node服务,iOS模拟器和web调试可正常使用localhost。
内容的提问来源于stack exchange,提问作者Myrmelo
相关产品推荐
相关产品推荐

