应直接从 request.form.files 显式获取文件集合,而非依赖 iformfile[] 参数绑定;需统一配置 formoptions.valuecountlimit 和 maxrequestbodysize,并确保前端 enctype="multipart/form-data" 且 name 匹配。

直接用 IFormFileCollection 接收多文件,别写 IFormFile[] 参数指望自动绑定——在 Minimal API 或部分 MVC 配置下它大概率为空,且静默失败。
为什么 Request.Form.Files 是空的或只有一两个文件
不是代码漏了逻辑,是请求根本没进模型绑定管道,或者被默认配置截断了。
- 前端
<input type="file" name="files" multiple>的name必须和后端读取时用的字段名一致(比如都叫files),大小写敏感 - 表单
enctype没设成"multipart/form-data",浏览器会当普通表单发,文件字段直接丢弃 - 上传 200 个文件时,
Request.Form.Files.Count却是 100 ——这是FormOptions.ValueCountLimit默认 1024 导致的:每个文件算一个表单字段,外加其他 hidden input 就超了,超出部分被静默丢弃 - Swagger 测试时填了字符串或 Base64,不是点「Choose File」选真实文件,
IFormFile必然为null
怎么安全、可靠地拿到全部文件
绕过模型绑定,显式从 Request.Form.Files 取值最稳。它不依赖参数名匹配,也不受 Action 方法签名影响。
- 用
var files = Request.Form.Files;直接获取集合,然后遍历 - 每个
file的FileName是客户端原始名(不可信),Name才是表单字段名(如"files") - 必须检查
file.Length > 0,空文件也会出现在集合里 - 不要用
files[0]这种写法取第一个——它可能根本不是你想要的那个,而且容易索引越界
大文件或批量上传时内存爆掉怎么办
IFormFile 默认全量加载进内存,几十个 50MB 文件就可能触发 OutOfMemoryException。流式处理是唯一可行路径。
- 绝对不要调
file.CopyToAsync(new MemoryStream())或file.OpenReadStream().ToArray() - 改用
file.OpenReadStream()获取只读流,再分块拷贝到磁盘或云存储 - 保存时用
Path.GetRandomFileName() + Path.GetExtension(file.FileName)生成新名,彻底脱离用户输入 - 目标路径必须用
Path.Combine(uploadRoot, safeName),且uploadRoot要是绝对路径(如_env.WebRootPath),提前Directory.CreateDirectory()
配置不调,代码写得再对也白搭
所有上传失败类错误(400/413/500)八成出在配置层,而不是你写的 Controller 里。
-
MaxRequestBodySize控制整个请求体上限,默认 30MB(.NET 6+ 在KestrelServerOptions中设) -
FormOptions.MultipartBodyLengthLimit控制 multipart 总长度,默认不限,但实际受前者约束 -
FormOptions.ValueCountLimit控制表单字段总数,默认 1024 —— 上传 300 个文件 + 5 个文本字段就超了 - 这些必须在
Program.cs的ConfigureServices里统一配,例如:builder.Services.Configure<formoptions>(options => { options.ValueCountLimit = 2000; options.MultipartBodyLengthLimit = 512 * 1024 * 1024; });</formoptions>
最容易被忽略的是:ValueCountLimit 和 MaxRequestBodySize 是两套独立限制,一个管“有多少个字段”,一个管“总共多大”,缺一不可。调完配置还得验证前端是否真传了那么多文件——别只信后端日志里的 Count。











