You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在基于Kestrel的ASP.NET Core服务中高效无缓存接收大文件上传

ASP.NET Core 3.1无缓存大文件上传问题

问题描述

我有一个运行在Kestrel上的ASP.NET Core 3.1 Web服务,需要接收大文件上传且不对文件做缓存,服务仅会将文件数据转发到另一台服务器。该服务运行在资源非常有限的节点上,因此上传数百MB及更大的文件时,完全不可能将其缓存到内存或磁盘中。
在我的测试中,ASP.NET Core似乎会强制缓存整个请求体,之后才会将数据传递到控制器。比如我编写的客户端以5MB/秒的速度上传100MB文件时,控制器路由要在请求启动约20秒后才会被调用,且此时请求方已经关闭了请求流。
该问题无论请求是否使用SSL都会出现。为了减少可变因素,我一直以未加密请求作为测试基准。我查阅的资料显示使用SSL可能会出现另一个问题:SSL证书协商需要缓冲整个请求,这个问题我之后再处理,目前我测试的是未加密HTTP端点,所以肯定不是这个原因导致的。
我要如何让控制器立即开始处理上传请求,无需在我的代码运行前在服务端找到存储整个文件的空间?

更新1

缓存会不会发生在客户端?我发现HttpWebRequest.AllowWriteStreamBuffering默认值为true,但即使将其设为false,我仍观察到缓冲行为。但我用Postman发送超大请求时似乎没有缓冲,这就奇怪了……

更新2

.NET Core根本没有实现AllowWriteStreamBuffering,它始终会缓冲请求。

更新3

在.NET Framework中,HttpClient是基于HttpWebRequest实现的;而在.NET Core(以及.NET 5/6)中,HttpWebRequest是基于HttpClient实现的。这种实现方式导致很难实现不可查找Stream的流式传输,因此.NET Core中的HttpWebRequest实现始终会做缓冲,但HttpClient的实现没有强制缓冲,所以直接使用HttpClient就能在所有环境下正常运行。
我在一个接收请求并转发的Web服务中观察到了异常缓冲行为,最终确认缓冲发生在作为客户端的转发代码中,而非ASP.NET Core前端。我也找到了解决该问题的方案,虽然需要编写不少代码,该方案适配服务的出站请求场景:使用BlobClient上传到Azure Blob Storage。
如果你有不可查找的流需要避免缓存,实现方案如下:

  • 创建Stream的子类,始终维护最近N字节的环形缓冲区,支持回退读取缓冲区内容直到追上写入头,底层流会被视为不可查找。
  • 在调用BlobClient.UploadAsync时搭配完整配置了TransferOptions的BlobUploadOptions使用该自定义流。TransferOptions中的属性可配置“块”大小(InitialTransferSize必须设置,且为了保证线性读取,MaximumConcurrency必须设为1)。如果发生错误UploadAsync需要重试时,仅需要回退到当前块的开头,因此环形缓冲区只需要等于单个块的大小即可。
    通过这套完整的解决方案,现在可以让ASP.NET服务通过HTTP PUT或POST接收文件,直接流式传输到Azure Blob Storage,没有任何中间缓冲,该服务可以运行在资源受限的节点上。

我现在申请关闭该问题,因为它已经不具备相关性了。

更新4

如果你使用的BlobUploadOptions中的TransferOptions同时配置了InitialTransferSize和MaximumTransferSize,BlobClient.UploadAsync会自动将流拆分为独立缓冲的块。你可以配置MaximumConcurrency,此时内存占用为MaximumConcurrency * MaximumTransferSize,分布在MaximumConcurrency个独立缓冲区中。这比我之前的实现简单得多,你只需要配置这两个参数即可,否则当CanSeek为false时,它会在发起请求前缓冲整个流。

内容的提问来源于stack exchange,提问作者Jonathan Gilbert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 03:54:01