IFormFile为null的根本原因是请求未进入模型绑定管道:必须使用multipart/form-data编码、禁用[FromBody]、参数名与FormData字段严格一致、Swagger需点“Choose File”上传。

IFormFile 参数为 null?不是代码错了,是请求根本没进模型绑定管道——必须用 multipart/form-data 提交,且不能带 [FromBody]。
为什么 IFormFile 总是 null
常见现象:前端 FormData 已 append,fetch 或表单提交后,断点根本进不去 Action,file 参数直接为 null。这不是后端漏写逻辑,而是请求被 ASP.NET Core 模型绑定器跳过了。
- 前端没设
enctype="multipart/form-data"(表单)或没用FormData构造(JS),浏览器会以application/x-www-form-urlencoded发送,文件字段被丢弃 - 后端参数加了
[FromBody],强制走 JSON 解析,而multipart数据无法被 JSON 模型绑定器识别,直接返回 400 - Swagger 测试时填了字符串或 Base64,而不是点「Choose File」——Swagger 不支持模拟二进制流,只认真实文件选择
- 前端
append('upload', file),后端却写IFormFile file,名称不匹配,绑定失败(默认 key 是file,不是upload或File)
怎么安全保存 IFormFile 到磁盘
别碰 file.FileName 拼路径——它由浏览器控制,可伪造为 ../../../web.config 或 shell.php.exe,直接导致目录穿越或执行攻击。
- 用
Path.GetExtension(file.FileName).ToLowerInvariant()提取后缀,只允许白名单如".jpg"、".pdf"、".xlsx";file.ContentType可辅助校验但不可信(易伪造) - 生成新文件名:
$"{Guid.NewGuid():N}{ext}"或Path.GetRandomFileName() + ext,彻底脱离用户输入 - 目标路径用
Path.Combine(uploadRoot, safeName)拼接,uploadRoot必须是绝对路径(如_env.WebRootPath),且提前调用Directory.CreateDirectory() - 大文件必须流式写入:
using var fs = File.Create(path); await file.CopyToAsync(fs);,别用file.OpenReadStream().ToArray()全读进内存
上传超 128 MB 就报 413?改三处配置才生效
ASP.NET Core 默认限制整个请求体为 128 MB,超过直接在 Kestrel 层拦截,连中间件都进不去,更别说你的 Controller。错误日志里常出现 Request body too large 或 HTTP 413.13。
- 在
Program.cs配 Kestrel:builder.Services.Configure(单位字节)(o => o.Limits.MaxRequestBodySize = 500_000_000); - 同时配 FormOptions:
builder.Services.Configure(o => { o.MultipartBodyLengthLimit = 500_000_000; o.ValueLengthLimit = 1024 * 1024; }); - 若部署在 Nginx 后面,必须同步加
client_max_body_size 500M;;IIS 则需web.config里 - 局部放宽可用
[RequestSizeLimit(500_000_000)],但[DisableRequestSizeLimit]仅限可信内网,且仍受反向代理限制
用 Stream 替代 IFormFile?除非你真需要绕过框架
IFormFile 是封装好的抽象,自带 FileName、ContentType、Length 和 OpenReadStream(),适合 95% 场景。直接接收 Stream 是底层玩法,代价高、易出错。
- 用
Stream参数必须加[DisableRequestSizeLimit],且要手动解析 multipart boundary,处理编码、字段分隔、文件头识别——MultipartReaderAPI 复杂,一不留神就漏数据 -
IFormFile默认有 128 MB 限制,但它是可配的;Stream虽“看起来更自由”,实则把所有边界检查、超时、内存管理甩给开发者 - 真正需要
Stream的场景极少:比如对接第三方分片上传协议、直传对象存储(OSS/S3)且不落地、或做实时流式病毒扫描 - 多文件上传别用
Stream[],老实用IFormFileCollection或IEnumerable,框架自动拆包
最常被忽略的其实是并发写入和磁盘权限:高并发下用 Guid.NewGuid() 保证文件名唯一,但目标目录得有写权限,且 Directory.CreateDirectory() 要在 CopyToAsync() 前执行——否则第一次上传会因父目录不存在而失败。











