https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-9.0
https://www.cnblogs.com/lindexi/p/19192031
在 ASP.NET Core 中处理文件上传时,框架提供了两种核心方法:Buffering(缓冲) 和 Streaming(流式传输)。选择正确的方法对应用的性能、资源消耗和可扩展性至关重要。
什么是 Buffering 模式?
Buffering 模式是文件上传的默认方式。在这种模式下,整个文件会被读取到内存或临时磁盘文件中,然后作为 IFormFile 对象提供给应用程序处理。
Buffering 的基本用法
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{if (file.Length > 0){var filePath = Path.Combine(_storagePath, Path.GetRandomFileName());using (var stream = System.IO.File.Create(filePath)){await file.CopyToAsync(stream);}}return Ok();
}
Buffering 模式的底层运作机制
当客户端上传文件时,Buffering 模式在底层经历以下流程:
- 网络接收:HTTP 请求数据通过网卡 DMA 传输到内核的 Socket 接收缓冲区
- 内核缓冲:ASP.NET Core 请求处理管道读取 HTTP 请求体到内核空间
- 内存缓冲(小文件):
- 如果文件 ≤ 64 KB,数据保留在内存中(托管堆)
- 此时数据从内核空间复制到用户空间(进程内存)
- 磁盘缓冲(大文件):
- 如果文件 > 64 KB,数据被写入临时文件
- 临时文件位置:
ASPNETCORE_TEMP环境变量指定的目录 - 这是为了避免大文件耗尽应用程序内存
- IFormFile 封装:框架将缓冲的数据封装为
IFormFile对象供开发者使用
关键点:在控制器方法执行前,整个文件已经完整存在于内存或临时磁盘中。
Buffering 的优缺点
优点:
- 代码简单直观,易于实现
- 适合小文件上传(< 64 KB 性能最佳)
- 完整的模型绑定支持和验证
缺点:
- 并发上传大量文件可能耗尽内存
- 大文件会占用磁盘空间(临时文件)
- 不适合超大文件(如视频、数据库备份)
重要配置
services.Configure<FormOptions>(options =>
{// 单个文件大小限制(默认 128 MB)options.MultipartBodyLengthLimit = 268435456; // 256 MB// 内存缓冲阈值(默认 64 KB)options.MemoryBufferThreshold = 65536;
});
什么是 Streaming 模式?
Streaming 模式直接处理 HTTP 请求流,无需先将整个文件缓冲到内存或磁盘。文件数据以数据块的形式逐步接收和处理。
Streaming 的基本概念
在 Streaming 模式下:
- 请求体被逐段读取(multipart sections)
- 每个数据块立即处理或写入目标位置
- 不依赖框架的模型绑定
Streaming 的实现示例
[HttpPost]
[DisableFormValueModelBinding] // 禁用默认模型绑定
public async Task<IActionResult> UploadStream()
{var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType),lengthLimit: 70);var reader = new MultipartReader(boundary, Request.Body);var section = await reader.ReadNextSectionAsync();while (section != null){var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);if (hasContentDispositionHeader && contentDisposition.IsFileDisposition()){// 直接流式写入目标位置var filePath = Path.Combine(_storagePath, Path.GetRandomFileName());using (var targetStream = System.IO.File.Create(filePath)){await section.Body.CopyToAsync(targetStream);}}section = await reader.ReadNextSectionAsync();}return Ok();
}
Streaming 的优缺点
优点:
- 内存占用极低,与文件大小无关
- 支持超大文件上传(GB 级别)
- 更好的并发处理能力
缺点:
- 实现复杂,需要手动解析 multipart 请求
- 需要禁用默认模型绑定
- 更容易出错,需要更多的错误处理
如何选择?
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 头像、图标(< 1 MB) | Buffering | 简单、性能好 |
| 文档、图片(1-10 MB) | Buffering | 在合理范围内 |
| 视频、备份(> 50 MB) | Streaming | 避免资源耗尽 |
| 高并发上传 | Streaming | 减少内存压力 |
| 需要立即处理 | Streaming | 边接收边处理 |
安全建议
无论使用哪种模式,都应该:
- 验证文件大小
if (file.Length > _fileSizeLimit)
{return BadRequest("文件过大");
}
- 验证文件扩展名
var ext = Path.GetExtension(fileName).ToLowerInvariant();
if (!_permittedExtensions.Contains(ext))
{return BadRequest("不支持的文件类型");
}
- 使用安全的文件名
// 永远不要直接使用用户提供的文件名
var safeFileName = Path.GetRandomFileName();
- 配置请求大小限制
[RequestSizeLimit(52428800)] // 50 MB
[RequestFormLimits(MultipartBodyLengthLimit = 52428800)]
public async Task<IActionResult> Upload(IFormFile file)
{// ...
}
总结
- Buffering 适合大多数常规场景,代码简单,性能足够
- Streaming 是处理大文件和高并发的必要手段
- 理解底层机制有助于做出正确的架构决策
- 始终要考虑安全性,无论选择哪种模式
选择正确的文件上传策略,能让你的应用在性能、可靠性和可扩展性之间取得最佳平衡。