坑 1:ViewModel 里嵌套的 IFormFile 数组不绑定
页面用一个列表来收集每行的附件,前端 name="Items[i].File" 命名,后端期望模型绑定到 List<RowVm>:
public class RowVm
{
public string Title { get; set; }
public IFormFile File { get; set; } // ❌ 不会被默认绑定
}
public class FormVm
{
public List<RowVm> Items { get; set; }
}
实际收到的 Items[i].File 始终为 null。原因:模型绑定器默认只认顶层的 IFormFile,嵌套对象里的不会自动映射。
解决方案有两个:
- 在 Action 上单独接收
IFormFileCollection,再按name自行匹配; - 写一个自定义
IModelBinder显式解析嵌套文件(适合复杂场景)。
最常用的就是方案 1:
[HttpPost]
public async Task<IActionResult> Upload(
[FromForm] FormVm vm,
IFormFileCollection files,
CancellationToken ct)
{
// 按 input 顺序 / name 自行匹配到 vm.Items
for (int i = 0; i < vm.Items.Count && i < files.Count; i++)
{
var file = files[i];
// ... 落盘等
}
return Ok();
}
坑 2:同名多文件,未选中的项不会为 null
前端是这种结构:
<input type="file" name="photos" multiple />
<input type="file" name="photos" multiple />
<input type="file" name="photos" multiple />
三个 input 共用 photos。后端用:
public IFormFile[] Photos { get; set; }
如果用户只选了第二个,前端只发出一个 photos 文件。不要假设 Photos 长度永远是 3,也不会有 null 填充:
// Photos 实际是 1 个元素,不是 3 个元素 + 2 个 null
foreach (var f in vm.Photos)
{
// f 一定不是 null,但数组长度可能跟前端 input 数量不一致
}
这跟传统
Request.Files 行为不同:旧 ASP.NET 时代会把空 input 留 null,ASP.NET Core 直接跳过。坑 3:上传大小被默认上限卡住
ASP.NET Core 默认 单文件 128 MB,整次请求 28.6 MB。Kestrel 上限独立配置。
全局调整(Program.cs):
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = 500 * 1024 * 1024; // 500 MB
});
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 500 * 1024 * 1024;
});
单个 Action 临时调整:
[HttpPost]
[RequestSizeLimit(500L * 1024 * 1024)] // 500 MB
[RequestFormLimits(MultipartBodyLengthLimit = 500L * 1024 * 1024)]
public async Task<IActionResult> Upload(IFormFile file) { ... }
如果部署在 IIS / Nginx 后面,三层都要改:Kestrel、ASP.NET Core、IIS(
web.config 的 requestLimits maxAllowedContentLength)/ Nginx(client_max_body_size)。否则任意一层都会先拒。顺手处理:上传安全
- 校验扩展名 + MIME(只信
ContentType不可靠,前端可伪造)。 - 落盘前不信任原文件名,用
Path.GetRandomFileName()重命名。 - 大文件用流式落盘:
await using var fs = System.IO.File.Create(path); await file.CopyToAsync(fs);。 - 对图片用 ImageSharp / System.Drawing 做解码校验,避免把恶意 SVG / 畸形 PNG 落库。
使用准则
- 上传相关属性放在 Action 参数最前面,不要塞进嵌套 ViewModel。
IFormFile[]永远按实际收到的元素遍历,不要指望与 input 数量一致。- 调大上传上限必须改三层:Kestrel / FormOptions / 反向代理。
- 大文件上传走分片或 tus 协议,不要把整个请求体塞进内存。