Flutter向Express上传视频及表单数据失败(Dio/Http)排查求助
Flutter上传视频+表单数据到Express服务器失败:请求体/文件为空
问题场景
调试模式下的Android设备,使用Flutter的Dio或http包向Express服务器上传视频文件及关联表单数据,但服务器日志显示文件和表单数据均为空。
Flutter代码(Dart)
Future<void> _uploadVideo() async { if (_video == null) return; // request.fields['email'] = "test@gmail.com"; // Add your email field // request.files.add(await http.MultipartFile.fromPath('file', _video!.path, // filename: basename(_video!.path))); final dio = Dio(); const uri = 'http://192.168.1.8:3000/upload'; var data = FormData.fromMap({ 'files': [ await MultipartFile.fromFile(_video!.path, filename: basename(_video!.path)) ], 'email': 'savioalwd@gmail.com' }); try { final response = await dio.request( uri, data: data, options: Options(method: 'POST', responseType: ResponseType.json), }
Express服务器代码
router.post('/upload', (req, res) => { console.log('Headers:', req.headers); // Log headers console.log('Body:', req.body); // Log form fields console.log('File:', req.files); console.log('File:', req.get('email')); // Log uploaded file });
服务器日志输出
headers: { 'user-agent': 'Dart/3.4 (dart:io)', 'content-type': 'multipart/form-data; boundary=--dio-boundary-1809956562', 'accept-encoding': 'gzip', 'content-length': '129', host: '192.168.1.8:3000' } Body: {} File: undefined File: undefined
问题现象
无论使用Dio还是http包,服务器端均显示:
- 请求头包含正确的multipart boundary;
- 请求体
req.body为空; - 文件
req.files为undefined。
已执行的排查步骤
- 确认
_video!.path文件路径正确; - 验证已设置正确的MIME类型和Content-Type;
- 检查Express服务器的multer配置,确保可处理multipart表单数据。
请求帮助
- 表单数据的构造或发送是否存在问题?
- 还有哪些额外的调试步骤可以解决此问题?
- 能否提供Flutter通过Dio或http向Express服务器上传文件及表单数据的可用示例?
解决方案及调试建议
1. 表单数据构造/发送的问题排查
(1)Express端multer配置缺失或错误
Express默认无法解析multipart/form-data格式,必须通过multer中间件处理。从日志看req.body和req.files为空,大概率是multer未正确生效:
const multer = require('multer'); // 自定义存储配置,需确保uploads文件夹已创建 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, './uploads'); }, filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); } }); const upload = multer({ storage: storage }); // 路由需绑定multer中间件:多文件用upload.array('files'),单文件用upload.single('file') router.post('/upload', upload.array('files'), (req, res) => { console.log('表单数据:', req.body); console.log('上传文件:', req.files); console.log('Email:', req.body.email); // 表单数据在req.body,不在headers res.status(200).json({ message: '上传成功', data: { files: req.files, body: req.body } }); });
注意:Flutter代码中文件字段名为files,所以multer需对应使用upload.array('files');若上传单文件,需将Flutter中的字段名改为file,同时用upload.single('file')。
(2)Flutter端代码问题
- 存在语法错误:
try块未闭合,缺少catch语句和结束括号,会导致代码无法正常执行; - 若上传单文件,无需用数组包裹
MultipartFile,直接传单个文件即可; - 确保已导入
basename方法:import 'package:path/path.dart' as path;,调用时用path.basename(_video!.path)。
2. 额外调试步骤
- 抓包验证请求内容:用Charles或Wireshark抓包,查看实际发送的请求体是否包含文件和表单数据,定位问题在客户端还是服务器端;
- 测试小文件上传:先上传txt等小文件,排除大视频文件的影响;
- 检查Android权限:确保应用已获取
READ_EXTERNAL_STORAGE或READ_MEDIA_VIDEO权限,否则无法读取视频文件; - Postman模拟请求:用Postman发送
multipart/form-data请求,验证服务器端multer是否正常解析; - 开启Dio日志:添加拦截器打印完整请求信息,确认数据构造是否正确:
dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
3. 可用示例代码
示例1:Dio上传单文件+表单数据
import 'package:dio/dio.dart'; import 'package:path/path.dart' as path; Future<void> uploadVideoWithDio(String videoPath) async { final dio = Dio(); // 添加日志拦截器调试 dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true)); const url = 'http://192.168.1.8:3000/upload'; try { FormData formData = FormData.fromMap({ 'email': 'savioalwd@gmail.com', 'file': await MultipartFile.fromFile( videoPath, filename: path.basename(videoPath), contentType: MediaType('video', 'mp4'),// 可选:指定视频MIME类型 ), }); Response response = await dio.post( url, data: formData, options: Options(responseType: ResponseType.json), ); print('上传成功:${response.data}'); } catch (e) { print('上传失败:$e'); } }
对应Express单文件处理代码:
const multer = require('multer'); const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, './uploads'); }, filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); } }); const upload = multer({ storage: storage }); router.post('/upload', upload.single('file'), (req, res) => { console.log('表单数据:', req.body); console.log('上传文件:', req.file); res.json({ message: '上传成功', email: req.body.email, file: req.file?.originalname }); });
示例2:http包上传文件+表单数据
import 'package:http/http.dart' as http; import 'package:path/path.dart' as path; Future<void> uploadVideoWithHttp(String videoPath) async { const url = 'http://192.168.1.8:3000/upload'; var request = http.MultipartRequest('POST', Uri.parse(url)); // 添加表单数据 request.fields['email'] = 'savioalwd@gmail.com'; // 添加文件 request.files.add(await http.MultipartFile.fromPath( 'file', videoPath, filename: path.basename(videoPath), contentType: MediaType('video', 'mp4'), )); try { var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); print('上传成功:${response.body}'); } catch (e) { print('上传失败:$e'); } }
对应Express代码与上述单文件处理代码一致。
内容的提问来源于stack exchange,提问作者xav
相关产品推荐
相关产品推荐

