在Web应用中,文件上传是一个常见功能,但当文件体积达到几百MB甚至数GB时,传统上传方式往往会出现超时、内存溢出、上传失败等问题。如何在.NET环境中实现稳定、高效的大文件上传,成为开发者必须解决的核心问题。
本文将从原理到实战,系统讲清楚.NET大文件上传的优化方案。
大文件上传为什么会出问题?
默认情况下,ASP.NET Core的文件上传存在几个天然限制:
- 请求体大小限制(Kestrel / IIS)
- 内存占用过高(IFormFile缓冲模式)
- 网络波动导致上传失败
- 单次请求时间过长导致超时
例如,缓冲上传会把整个文件加载到内存中,一旦文件过大或并发较高,很容易拖垮服务器 。
核心优化思路(必须掌握的4个方向)
1. 分片上传(Chunk Upload)
这是最常见、也是最重要的优化手段。核心思想是将大文件拆分成多个小块(如5MB/10MB),逐个上传,最后在服务端合并。
优势:
- 降低单次请求压力
- 支持断点续传
- 提升上传稳定性
实现要点:
- 前端使用 File.slice() 分片
- 每个分片带上索引(chunkIndex)
- 服务端按顺序合并文件
C#实现代码示例:
[HttpPost]
public async Task<IActionResult> UploadChunk(IFormFile file, int chunkIndex)
{
var path = Path.Combine("temp", $"{chunkIndex}.part");
using var stream = new FileStream(path, FileMode.Create);
await file.CopyToAsync(stream);
return Ok();
}
分片上传本质是把大请求拆成多个小请求,从根本上解决性能瓶颈 。
2. 流式上传(Streaming Upload)
如果说分片是拆文件,那流式上传就是边传边写。不把文件加载到内存,而是直接以流的形式写入磁盘。
Controller 代码示例:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
private readonly string _uploadPath = "uploads";
[HttpPost("stream")]
[DisableFormValueModelBinding] // 关键:禁用默认模型绑定
public async Task<IActionResult> UploadStream()
{
// 1. 获取 boundary
var boundary = HeaderUtilities.RemoveQuotes(
MediaTypeHeaderValue.Parse(Request.ContentType).Boundary
).Value;
var reader = new MultipartReader(boundary, Request.Body);
MultipartSection section;
while ((section = await reader.ReadNextSectionAsync()) != null)
{
var contentDisposition = section.GetContentDispositionHeader();
// 2. 判断是否文件
if (contentDisposition != null && contentDisposition.IsFileDisposition())
{
var fileName = Path.GetRandomFileName();
var filePath = Path.Combine(_uploadPath, fileName);
// 3. 流式写入(核心)
using var targetStream = System.IO.File.Create(filePath);
await section.Body.CopyToAsync(targetStream);
}
}
return Ok("上传成功");
}
}
优势:
- 极低内存占用
- 支持超大文件(GB级)
适用场景:
- 单文件上传
- 内网/稳定网络环境
微软官方明确建议:大文件应使用流式处理,而不是缓冲方式 。
3. 断点续传(Resume Upload)
现实中,上传经常会中断(断网、刷新、崩溃)。
优化方案:
- 服务端记录已上传分片
- 前端查询进度,仅上传缺失部分
关键点:
- 文件唯一标识(MD5 / Hash)
- 分片状态记录(数据库或缓存)
这样可以避免重复上传,大幅提升用户体验 。
4. 并发与异步优化
大文件上传不仅是文件问题,还是并发问题。
优化方向:
- 使用 async/await 异步IO
- 控制分片并发数量(如3~5个)
- 避免磁盘频繁IO竞争
- 使用队列或限流策略
合理的并发控制可以显著提升吞吐能力,同时避免服务器过载。
服务端关键配置优化
1. 放开上传大小限制
services.Configure<IISServerOptions>(options =>
{
options.MaxRequestBodySize = long.MaxValue;
});
否则会出现HTTP 404.13 - 请求体过大的错误。
2. Kestrel配置
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = long.MaxValue;
});
- 分片临时存储(Temp目录)
- 定时清理无效分片
- 最终文件写入独立存储(如OSS/MinIO)
进阶优化(提升一个量级)
如果你的网站有较高并发或商业需求,可以进一步优化:
1. 秒传机制
- 计算文件Hash
- 服务端已存在则直接返回成功
2. 动态分片大小
- 网络好 → 10MB/片
- 网络差 → 1MB/片
3. 上传进度与用户体验
- 实时进度条
- 失败自动重试
4. 云存储直传
- 前端直传OSS(阿里云、S3)
- 服务端只做签名校验
推荐技术方案组合(实战总结)
如果你要做一个稳定可商用的上传系统,推荐:
- 基础版(适合普通网站):分片上传 + 文件合并,简单断点续传
- 进阶版(高并发):分片 + 流式写入,Redis记录上传进度,限流 + 异步队列
- 企业级方案:前端直传云存储,秒传 + CDN加速,分布式存储
总结
.NET并不是不能处理大文件,而是默认配置和方式不适合。真正高效的大文件上传,本质是三点:
- 不一次性处理(分片)
- 不占用内存(流式)
- 不重复上传(断点续传)
掌握这三点,你就可以构建一个稳定支撑GB级文件上传的系统。