Dart Frog 默认不支持文件上传,因其设计遵循“轻量+显式”原则,需开发者手动解析 multipart/form-data 请求体;必须提取 boundary、用 MimeMultipartTransformer 流式处理、校验文件大小并安全写入磁盘。

Dart Frog 本身不内置文件上传解析能力,必须手动处理 Multipart/form-data 请求体——这是绝大多数开发者踩坑的起点。
为什么 Dart Frog 默认不支持文件上传?
Dart Frog 的设计哲学是“轻量+显式”,它把请求体解析(尤其是 multipart)交由开发者决定,避免隐式依赖和边界模糊。这意味着:context.request.body() 返回的是原始字节流,不是解析好的字段或文件对象。
- 直接调用
await context.request.body()会得到一整块二进制数据,无法直接提取文件名、内容或字段 - 你不能像 Express 或 Shelf 那样用
request.files直接访问文件 —— Dart Frog 没有这个属性 - 必须自己解析
Content-Type中的boundary,再按规范切分 multipart 区段
如何在 Dart Frog 路由中安全解析上传文件?
推荐使用 package:mime + 手动流式解析,避免内存爆炸(尤其对大文件)。关键步骤如下:
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
- 先检查
context.request.headers['content-type']是否以multipart/form-data开头,并提取boundary - 用
MimeMultipartTransformer将原始请求体流转换为MimeMultipart流 - 逐个
await for (var part in ...)处理每个部分,通过content-disposition判断是否为文件字段 - 对文件部分,用
part.contentLength做大小校验,再用part.readBytes()或流式写入磁盘(推荐File.openWrite())
import 'dart:io'; import 'package:dart_frog/dart_frog.dart'; import 'package:mime/mime.dart'; FutureonRequest(RequestContext context) async { final headers = context.request.headers; final contentType = headers['content-type']; if (contentType == null || !contentType.startsWith('multipart/form-data')) { return Response(statusCode: 400, body: 'Expected multipart/form-data'); } final boundary = HeaderValue.parse(contentType).parameters['boundary']; if (boundary == null) { return Response(statusCode: 400, body: 'Missing boundary'); } final bodyStream = context.request.read(); final transformer = MimeMultipartTransformer(boundary); try { await for (final part in bodyStream.transform(transformer)) { final disposition = part.headers['content-disposition']; if (disposition != null && disposition.contains('filename=')) { final filename = disposition.split('filename="')[1]?.split('"')[0] ?? 'unknown'; final bytes = await part.readBytes(); // 示例:保存到临时目录 final file = File('/tmp/uploaded_$filename'); await file.writeAsBytes(bytes); return Response(body: 'Saved as ${file.path}'); } } return Response(statusCode: 400, body: 'No file part found'); } catch (e) { return Response(statusCode: 400, body: 'Parse error: $e'); } }
客户端传参与常见错误对照
前端必须严格匹配后端预期,否则 boundary 解析失败,整个请求变成乱码字节流:
- HTML 表单要加
enctype="multipart/form-data",且的name必须和服务端查找的字段名一致(如filename=后的 key) - 用
fetch或Dio上传时,务必用FormData构造体,不要手动拼body字符串 - 错误现象:
Invalid boundary in multipart或解析后part.headers为空 → 检查前端是否漏传Content-Type,或 Dart Frog 是否被中间件(如 CORS)篡改了 header
真正麻烦的不是解析逻辑,而是边界处理:文件名编码(中文乱码)、空文件、超大文件阻塞事件循环、临时文件清理——这些都得自己补全,Dart Frog 不代劳。









