C#文件上传实现 C#如何处理HTTP文件上传

3次阅读

asp.net core 中用 iformfile 接收上传文件最直接:参数声明为 iformfile(单文件)或 iformfilecollection(多文件),匹配前端 input 的 name 属性,禁用 [frombody],校验文件合法性并流式保存。

C#文件上传实现 C#如何处理HTTP文件上传

ASP.NET Core 中用 IFormFile 接收上传文件最直接

http 文件上传在 ASP.NET Core 中默认走表单 multipart/form-data,后端不用手动解析原始流——框架已封装好。核心是用 IFormFile 类型绑定上传的单个文件,它自带 FileNameContentTypeLengthOpenReadStream() 等关键属性和方法。

常见错误是直接用 Stringbyte[] 去绑定上传字段,这会导致模型绑定失败,请求体被丢弃,返回 400 或空值。

  • 控制器方法参数必须声明为 IFormFile(单文件)或 IFormFileCollection(多文件)
  • 前端 <input type="file">name 属性值需与参数名一致,例如 <input name="file"> 对应 [FromForm] IFormFile file
  • 不要在参数上加 [FromBody],multipart 数据不走 json 解析管道
  • 若用 Swagger 测试,需在 Startup.csProgram.cs 中启用 AddControllersWithViews() 并确保 EnableRequestSizeLimit 足够大(默认仅 128MB)

大文件上传必须配置 RequestSizeLimitDisableRequestSizeLimit

ASP.NET Core 默认限制整个请求体大小为 128MB,超过会直接返回 404.13(Request Entity Too Large)。这不是业务逻辑能捕获的异常,而是在中间件早期就被拦截了。

两种处理方式:

  • 对特定 Action 放宽限制:在 Controller 方法上加 [RequestSizeLimit(500_000_000)](单位字节)
  • 彻底禁用限制(仅限可信内网场景):加 [DisableRequestSizeLimit],但必须配合反向代理(如 nginx)的 client_max_body_size 设置,否则仍会失败
  • 全局修改需在 WebHostBuilder 阶段调用 UseKestrel(o => o.Limits.MaxRequestBodySize = NULL),但不推荐

注意:MaxRequestBodySize = null 表示不限制,但 Kestrel 实际仍有底层 socket 缓冲区约束,超大文件仍建议分块上传。

保存文件前务必校验 file.Lengthfile.FileName

IFormFile 不代表文件一定存在或合法。用户可能提交空文件、恶意扩展名、路径遍历字符串(如 ../../web.config),或伪造 Content-Type

  • 先检查 file.Length == 0,跳过空上传
  • Path.GetExtension(file.FileName) 提取扩展名,白名单比对(如只允许 .pdf.jpg),别信 file.ContentType
  • Path.GetFileName(file.FileName) 获取干净文件名,避免目录穿越
  • 生成唯一文件名(如 Guid.NewGuid() + ext),防止覆盖或竞态写入
  • 写入磁盘前确保目标目录存在且进程有写权限,否则抛 UnauthorizedAccessException

流式保存比 CopyToAsync 更适合大文件和内存敏感场景

IFormFile.CopyToAsync(Stream) 是最常用方式,但它会把整个文件读进内存再刷出,对 >100MB 的文件容易触发 GC 压力甚至 OutOfMemoryException

更稳妥的做法是手动控制流:

using var stream = file.OpenReadStream(); using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 81920, useAsync: true); await stream.CopyToAsync(fs);

关键点:

  • 显式指定 bufferSize(如 80KB),避免默认 8KB 在高吞吐下频繁调度
  • useAsync: true 启用真正的异步 I/O,避免线程池阻塞
  • 不要用 file.CopyTo()(同步版),它在高并发时会快速耗尽线程池
  • 若需病毒扫描或内容校验,应在流复制过程中逐块处理,而非全量加载

真正难的不是“怎么传”,而是上传中途断连、重复提交、临时文件清理、存储一致性这些边界问题——它们不会出现在 Hello World 示例里,但上线后第一个凌晨报警往往就来自这儿。

text=ZqhQzanResources