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

Flutter WEB端如何向API上传文件?

Flutter Web API文件上传报错解决方案

实现前提

需要引入两个适配Web的第三方依赖,在pubspec.yaml中添加如下配置:

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.1 # 网络请求库,使用最新稳定版即可
  file_picker: ^6.1.1 # 文件选择库,已完成全平台适配

完整可运行代码实现

import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:dio/dio.dart';

class FileUploadPage extends StatefulWidget {
  const FileUploadPage({super.key});

  @override
  State<FileUploadPage> createState() => _FileUploadPageState();
}

class _FileUploadPageState extends State<FileUploadPage> {
  double _uploadProgress = 0;

  // 选择本地文件
  Future<PlatformFile?> _pickLocalFile() async {
    FilePickerResult? pickResult = await FilePicker.platform.pickFiles(
      // 可根据需求修改文件类型限制
      type: FileType.custom,
      allowedExtensions: ['pdf', 'png', 'jpg', 'doc'],
      // Web端必须开启withData,才能直接获取文件二进制流
      withData: true,
    );
    if (pickResult != null && pickResult.files.isNotEmpty) {
      return pickResult.files.first;
    }
    return null;
  }

  // 上传文件到API
  Future<void> _uploadToApi(PlatformFile file) async {
    final Dio dio = Dio();
    final FormData formData = FormData.fromMap({
      // 补充接口要求的其余表单参数,示例为userId
      "userId": "10001",
      // 文件字段名需和后端约定保持一致,示例为uploadFile
      "uploadFile": MultipartFile.fromBytes(
        file.bytes!,
        filename: file.name,
      ),
    });

    try {
      await dio.post(
        // 替换为实际的接口地址
        "https://your-api-domain.com/upload",
        data: formData,
        options: Options(
          // 如有鉴权需求,补充对应的请求头
          headers: {
            "Authorization": "Bearer your_token_here",
          },
        ),
        onSendProgress: (sentBytes, totalBytes) {
          if (totalBytes != -1) {
            setState(() {
              _uploadProgress = sentBytes / totalBytes;
            });
          }
        },
      );
      // 上传成功逻辑
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("上传成功")));
      }
    } on DioException catch (e) {
      // 错误排查输出
      debugPrint("错误码: ${e.response?.statusCode}");
      debugPrint("接口返回错误信息: ${e.response?.data}");
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("上传失败:${e.response?.statusMessage}")));
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("文件上传示例")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                PlatformFile? file = await _pickLocalFile();
                if (file != null) {
                  await _uploadToApi(file);
                }
              },
              child: const Text("选择并上传文件"),
            ),
            const SizedBox(height: 20),
            if (_uploadProgress > 0)
              SizedBox(
                width: 200,
                child: LinearProgressIndicator(
                  value: _uploadProgress,
                  backgroundColor: Colors.grey[200],
                  valueColor: const AlwaysStoppedAnimation<Color>(Colors.blue),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

常见报错排查项

  • 平台兼容问题:Flutter Web不支持本地文件路径读取,禁止使用MultipartFile.fromFile方法,该方法仅适配移动端,Web端必须使用MultipartFile.fromBytes传入文件二进制流
  • 跨域错误:如果控制台报CORS相关错误,需要后端配置跨域规则,开放OPTIONS请求权限,允许前端域名、自定义请求头和POST请求方法
  • 请求头错误:不要手动设置Content-Type: multipart/form-data,Dio构造FormData时会自动生成带boundary的合法请求头,手动设置会导致boundary丢失,后端无法解析表单数据
  • 参数不匹配:确认文件字段名、其余表单参数、文件名后缀和后端要求完全一致,缺失文件名将导致大部分后端框架识别文件失败

内容的提问来源于stack exchange,提问作者Muhammad Shafique

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:48:00