Flutter向Node.js+Multer传图时req.file为undefined问题求助
Flutter上传图片到Node.js+Multer服务报错排查
我在开发Flutter应用通过MultipartRequest向Node.js+Multer服务上传图片的功能时遇到问题:Postman调用接口正常,但Flutter端报错,服务器抛出异常。以下是相关代码及错误信息:
Flutter代码
print("TEST1"); var request = http.MultipartRequest("POST", Uri.parse(url)); print("TEST2"); request.fields['request'] = jsonEncode<String, int>{'todoId': todoId}; print("TEST3"); try { File file = await File.fromRawPath(result.files.single.bytes!); print("TEST4"); http.MultipartFile file_forrequest = http.MultipartFile('filetoupload', file.readAsBytes().asStream(), file.lengthSync(), filename: file.path.split("/").last); request.files.add(file_forrequest); } catch (e) { print("ERROR: " + e.toString()); } print("TEST5"); request.headers.addAll<String, String>{ "Authorization": "Bearer " + token.toString(), "Content-Type": "application/json" }; print("TEST6"); var response = await request.send(); print("TEST7"); print(response.statusCode); String res = await response.stream.bytesToString(); print(res);
Node.js接口代码
app.post(`${vorPath}/otherFileToToDo`, jsonParser, berechtigungmitgruppepruefen, uploadFileToDo("filetoupload"), async (req, res) => { console.log("FILES:"); console.log(req.files); console.log("__"); const request = JSON.parse(req.body.request); const todoId = request.todoId; const peopleId = req.user.peopleId; const filePath = req.file.path; try { const todos = await prisma.todos.findMany({ where: { id: todoId, zustaendige: { some: { peopleId: peopleId } } } }); if (todos.length == 1) { const todo = todos[0]; const otherFilesPaths = todo.otherFilesPaths; otherFilesPaths.push(filePath); await prisma.todos.update({ where: { id: todoId }, data: { otherFilesPaths: otherFilesPaths } }) res.sendStatus(200); } else { const todos_2 = await prisma.todos.findMany({ where: { id: todoId } }) if (todos_2.length == 1) { res.sendStatus(401); } else { res.sendStatus(404); } } } catch (error) { console.log("ERROR: " + error); res.sendStatus(500); } res.sendStatus(200); })
uploadFileToDo函数代码
function uploadFileToDo(file) { const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, '/mnt/sknasserver/todofiles/'); }, filename: (req, file, cb) => { const withoutLastFour = file.originalname.slice(0, -4); const lastFour = file.originalname.slice(-4); const newFileName = withoutLastFour + "-" + password_generator.generate({ length: 10, symbols: false, numbers: true }) + lastFour; cb(null, newFileName); }, }); const upload = multer({ storage: storage }); const uploadsingle = upload.single(file); return (req, res, next) => { uploadsingle(req, res, (err) => { if (err) { console.error('Error during file upload:', err); return res.status(400).send('Error during file upload: ' + err.message); } console.log('File uploaded successfully:', req.file); req.file.path = "/todofiles/" + req.file.filename; next(); }); }; }
服务器控制台日志
file App listening on port 5000 null File uploaded successfully: undefined /home/schemannkurier/api/sk_backend/api.js:108 req.file.path = "/todofiles/" + req.file.filename; ^ TypeError: Cannot read properties of undefined (reading 'filename') at /home/schemannkurier/api/sk_backend/api.js:108:54 at done (/home/schemannkurier/api/sk_backend/node_modules/multer/lib/make-middleware.js:45:7) at indicateDone (/home/schemannkurier/api/sk_backend/node_modules/multer/lib/make-middleware.js:49:68) at Multipart.<anonymous> (/home/schemannkurier/api/sk_backend/node_modules/multer/lib/make-middleware.js:166:7) at Multipart.emit (node:events:514:28) at Multipart.emit (node:domain:488:12) at emitCloseNT (node:internal/streams/destroy:132:10) at process.processTicksAndRejections (node:internal/process/task_queues:81:21) Node.js v20.10.0
Flutter调试日志
TEST1 TEST2 TEST3 TEST4 ERROR: Unsupported operation: _Namespace TEST5 TEST6 TEST7 502 <html> <head><title>502 Bad Gateway</title></head> <body> <center><h1>502 Bad Gateway</h1></center> <hr><center>nginx/1.25.5</center> </body> </html> 6 <!-- a padding to disable MSIE and Chrome friendly error page -->
问题分析与修复步骤
1. Flutter端核心错误修复
Unsupported operation: _Namespace错误:File.fromRawPath用于解析原生文件路径的字节,不能直接用文件内容字节创建File对象。直接用字节数组生成MultipartFile即可,无需转成File。- 手动设置
Content-Type: application/json:MultipartRequest会自动生成multipart/form-data请求头,手动覆盖会导致Multer无法解析表单数据,这是服务器req.file为undefined的根本原因,需删除该设置。
修复后的Flutter代码:
print("TEST1"); var request = http.MultipartRequest("POST", Uri.parse(url)); print("TEST2"); request.fields['request'] = jsonEncode({'todoId': todoId}); print("TEST3"); try { Uint8List fileBytes = result.files.single.bytes!; print("TEST4"); http.MultipartFile file_forrequest = http.MultipartFile.fromBytes( 'filetoupload', fileBytes, filename: result.files.single.name, ); request.files.add(file_forrequest); } catch (e) { print("ERROR: " + e.toString()); } print("TEST5"); // 仅添加Authorization头,不手动设置Content-Type request.headers.addAll({ "Authorization": "Bearer " + token.toString(), }); print("TEST6"); var response = await request.send(); print("TEST7"); print(response.statusCode); String res = await response.stream.bytesToString(); print(res);
2. Node.js端增加防护逻辑
服务器端需先判断req.file是否存在,避免空指针崩溃:
修改uploadFileToDo函数:
return (req, res, next) => { uploadsingle(req, res, (err) => { if (err) { console.error('Error during file upload:', err); return res.status(400).send('Error during file upload: ' + err.message); } if (!req.file) { console.error('No file uploaded'); return res.status(400).send('No file uploaded'); } console.log('File uploaded successfully:', req.file); req.file.path = "/todofiles/" + req.file.filename; next(); }); };
3. 额外注意事项
- 确保Flutter端文件选择插件(如image_picker)返回的
bytes是有效文件字节数组。 - 服务器端
/mnt/sknasserver/todofiles/目录需配置写入权限,避免Multer无法保存文件。
内容的提问来源于stack exchange,提问作者ennolichmannse
相关产品推荐
相关产品推荐

