必须手动解析 multipart/form-data 原始字节流:先从 Content-Type 提取 boundary,构造分隔符;分块读取 Body,用字节比较定位 part 边界和 header 结束(\r\n\r\n);安全解析 Content-Disposition 中 name/filename(支持引号、RFC 5987 编码);依 filename 和 Content-Type 区分字段类型,文件 body 须原样保存;禁用 ReadLine(),避免二进制内容误截断。

怎么手动解析 multipart/form-data 的原始字节流
不能靠 HttpRequest.Form 或 IFormCollection,就得从原始 Request.Body 读字节,再按 RFC 7578 规则拆解。核心是先提取 boundary,再用它切分出每个 part。
实操建议:
- 从
Content-Type请求头里用正则提取boundary=...,注意可能带双引号,比如boundary="----WebKitFormBoundaryabc123" - 构造分隔符行:开头两个破折号 + boundary + 可选的两个破折号(结尾 part 会多两个),即
--{boundary}和--{boundary}-- - 用
Stream.ReadAsync分块读,别一次性ToArray()—— 大文件直接 OOM - 每个 part 的头部以
\r\n\r\n结尾,前面是 headers(如Content-Disposition、Content-Type),后面是 body;需逐行解析 header,区分大小写不敏感
Content-Disposition 字段怎么安全提取文件名和字段名
这个 header 是识别字段类型的关键,但格式松散、编码混乱,容易误判。
常见错误现象:
- 直接
.Split(';')然后找filename=,结果遇到filename*=UTF-8''xxx.jpg就崩了 - 忽略引号包裹的值,把
name="file"; filename="a b.txt"错拆成a和b.txt - 没处理 RFC 5987 编码(
filename*=),中文文件名变成乱码
实操建议:
- 用正则匹配
name="([^"]*)"和filename="([^"]*)",优先取带引号的完整值 - 若遇到
filename\*=(.+?)''(.+),用WebUtility.UrlDecode解码右侧,并按指定 charset 转字符串(默认 UTF-8) - 字段名为空或只有空格?跳过,不是文件上传项
怎么区分普通字段和文件字段并分别处理
仅靠 Content-Disposition 不够,必须结合是否有 filename、是否有 Content-Type、body 是否二进制来综合判断。
使用场景差异:
- 普通字段:无
filename,body 是纯文本(可能含\r\n),可直接用Encoding.UTF8.GetString() - 文件字段:有
filename,且通常带Content-Type(如image/png),body 应按原始字节保存,**绝不能用字符串解码** - 边界风险:part body 末尾自带
\r\n,别把它当内容写进文件
性能影响:
- 对小字段,内存中暂存没问题;对大文件,应边读边写入磁盘或流式处理,避免缓冲区膨胀
- 不要反复
Seek(0)重读 stream —— 原始RequestBody通常不可回溯,需自己缓存或用MemoryStream中转
为什么 ReadLine() 在 multipart 解析中基本不能用
因为 HTTP body 是二进制流,不是纯文本,ReadLine() 依赖 \r\n 切行,但文件内容本身可能含任意字节,包括 \r\n,会导致提前截断或解析错位。
容易踩的坑:
- 用
StreamReader.ReadLine()解析 header —— 看似可行,但一旦某个 part 的Content-Type是text/plain且内容含\r\n,后续解析全乱 - 假设所有换行都是
\r\n,忽略部分客户端发\n(虽不合规,但真实存在) - 没考虑 boundary 出现在文件内容里的可能性(极低,但理论上需扫描 escape)
实操建议:
- header 解析用
Span找.IndexOf \r\n\r\n,而不是按行读 - body 提取用
Stream.Position+Length计算偏移,配合boundary字节序列做滑动窗口查找 - 边界检测必须用字节比较(
ReadOnlySpan),别转 string 再比.SequenceEqual
最麻烦的其实是 boundary 和文件内容重叠的极端情况,实际中极少发生,但如果你的业务要存用户传的 zip 或 pdf,就得加校验逻辑——比如检查 boundary 是否出现在前 1KB 之后,否则拒绝上传。










