React Native iOS端图片上传至Express后端未保存至文件夹求助
大家好,我正在开发一个支持用户上传头像的前后端应用:
- 后端:Node.js + Express.js,用Multer处理文件上传
- 前端:React Native,使用
react-native-image-picker选择图片
现在遇到的问题是:iOS端上传时,服务器的images文件夹里没有保存文件,但用Postman直接发POST请求却能成功。我曾猜测是文件扩展名的问题,但调整后还是无效。前端会弹出“Photo updated succesfully!”的提示,也没有报错信息,实在找不到原因,恳请帮忙排查。
后端代码(Multer配置及路由)
const storage = multer.diskStorage({ destination : function(req, file, cb ){ cb(null,'./images/'); }, filename : function(req,file,cb){ cb(null, Date.now() + file.originalname); } }) const upload = multer({ storage : storage, // filter only valid image files fileFilter: function(req,file,cb){ let ext = path.extname(file.originalname); if(ext != '.png' && ext != '.jpg' && ext !='jpeg'){ return cb('Only images are allowed ') } cb(null, true) }, limits:{fieldSize: 25*1024*1024} }) // route for photo upload test router.post('/upload',upload.single('photo'), (req,res)=> { console.log('file',req.files); //console.log('body', req.body); res.status(200).json({ message:'success!', }); });
React Native端代码
const createFormData = (photo, body) =>{ const data = new FormData(); data.append('photo', { name:photo.fileName, type:photo.type, uri : Platform.OS === 'android' ? photo.uri : photo.uri.replace('file://', ''), // to be sure it works with android }); Object.keys(body).forEach(key => { data.append(key, body[key]); }); return data; } handleUploadPhoto = () =>{ fetch('http://localhost:5050/api/new/upload',{ method: 'POST', body: createFormData(this.state.dataPhoto,{userId : 'test'}), }) .then(response => response.json()) .then(response => { console.log('upload succes', response); alert('Photo updated succesfully!') }) .catch(error => { console.log('upload failed', error); alert('Upload is not possible right now!'); }) };
可能的解决方案
1. 修正后端req.files的错误引用
你用的是upload.single('photo'),这意味着后端接收的是单个文件,应该用req.file(单数)而不是req.files(复数)。当前代码里打印req.files会得到undefined,根本无法确认服务器是否收到文件。
修改后端的日志代码:
console.log('file', req.file); // 替换req.files为req.file
如果打印结果还是undefined,说明前端的FormData格式存在问题。
2. 修复iOS端的URI处理逻辑
iOS端从相册选择的图片URI通常是ph://开头,而不是file://,你当前的替换逻辑对这种情况无效。可以调整URI处理:
uri: Platform.OS === 'ios' ? photo.uri.replace('ph://', '') : photo.uri.replace('file://', '')
另外,建议确认photo.type是否为正确的MIME类型(比如image/jpeg、image/png),如果react-native-image-picker返回的type不准确,手动指定也能解决问题:
type: photo.type || 'image/jpeg'
3. 完善Multer的错误处理逻辑
你的文件过滤器错误传递方式不符合Multer规范,应该用Error对象传递错误,否则请求会被当作正常处理,但文件不会保存。同时添加全局错误捕获,让前端能感知到错误:
// 修改文件过滤器 fileFilter: function(req, file, cb) { let ext = path.extname(file.originalname).toLowerCase(); // 转小写避免大小写问题 if(ext !== '.png' && ext !== '.jpg' && ext !== '.jpeg'){ return cb(new Error('Only images are allowed')); // 使用Error对象 } cb(null, true); } // 路由添加错误处理中间件 router.post('/upload', upload.single('photo'), (req, res) => { console.log('file', req.file); res.status(200).json({ message:'success!' }); }, (err, req, res, next) => { // 捕获Multer的错误并返回给前端 res.status(400).json({ message: err.message }); });
4. 确保服务器images文件夹的权限和路径正确性
相对路径./images/可能因为服务器启动目录不同导致找不到文件夹,建议改用绝对路径,同时确保文件夹存在且有写入权限:
const fs = require('fs'); const path = require('path'); // 生成绝对路径 const imagesDir = path.join(__dirname, '../images/'); // 检查文件夹是否存在,不存在则创建 if (!fs.existsSync(imagesDir)) { fs.mkdirSync(imagesDir, { recursive: true }); } // 更新storage的destination const storage = multer.diskStorage({ destination: function(req, file, cb) { cb(null, imagesDir); }, // ...其他配置 });
5. 确认iOS网络访问权限
如果用真机测试,localhost无法访问服务器,需要替换为电脑的局域网IP(比如http://192.168.x.x:5050)。另外iOS 14+需要在Info.plist中添加HTTP请求权限:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
内容的提问来源于stack exchange,提问作者El Pandario

