You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native iOS端图片上传至Express后端未保存至文件夹求助

问题:React Native iOS端上传头像至Node.js+Express服务器无文件保存(Postman正常)

大家好,我正在开发一个支持用户上传头像的前后端应用:

  • 后端: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:28:22