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

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表单数据。

请求帮助

  1. 表单数据的构造或发送是否存在问题?
  2. 还有哪些额外的调试步骤可以解决此问题?
  3. 能否提供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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:35:01