C# 文件上传的协议选择 C#在HTTP/1.1, HTTP/2, gRPC中如何选择文件传输方案

来源:这里教程网 时间:2026-02-21 17:42:05 作者:

HTTP/1.1 上传大文件时为什么卡在 100-continue?

因为默认启用

Expect: 100-continue
,服务端没及时响应确认,客户端就挂起等待。尤其在 Nginx、IIS 或某些反向代理后面,这个行为常被忽略或拦截。

实操建议:

客户端侧:设置
request.ServicePoint.Expect100Continue = false
HttpWebRequest
)或用
HttpClient
时在
HttpClientHandler
中设
Expect100ContinueTimeout
为 0
服务端侧:ASP.NET Core 默认不处理 100-continue,但 Kestrel 可通过
WebHostBuilder.UseKestrel(o => o.Limits.MaxRequestLineSize)
类似配置间接影响;更关键的是确保中间件(如
UseHttpsRedirection
)不干扰初始请求头
上传超 100MB 时,务必同步调大
maxAllowedContentLength
(IIS)、
client_max_body_size
(Nginx)和 ASP.NET Core 的
FormOptions.ValueLengthLimit

HTTP/2 下
HttpClient
上传文件会复用连接,但别指望自动分块重试

HTTP/2 天然支持多路复用和头部压缩,上传多个小文件时延迟明显降低;但单个大文件上传仍是一次性流式发送,断连后不会自动续传 —— 这和协议无关,是

HttpClient
实现决定的。

实操建议:

确认 .NET 版本 ≥ 5.0,且服务端明确支持 HTTP/2(TLS 1.2+、ALPN 协商成功),否则
HttpClient
会静默降级到 HTTP/1.1
不要依赖
HttpClient
自带的“重试逻辑”处理上传中断;需自己实现分片(如按 5MB 切
Stream
)+ 断点记录 + 幂等接口(例如带
X-Upload-ID
X-Chunk-Index
避免在
using var stream = File.OpenRead(...)
内直接传给
PostAsync
—— 若上传中途失败,
stream
已被释放,无法重试;应打开后缓存位置,或改用
FileStream
并控制
leaveOpen: true

gRPC 不适合直接传大文件,但可以封装成流式上传契约

gRPC 基于 HTTP/2,天生支持双向流,但它默认对单条消息有 4MB 限制(

MaxReceiveMessageSize
),且二进制 payload 直接序列化进 Protobuf,没有原生文件元数据(如 filename、content-type)支持。

实操建议:

stream UploadFile(stream UploadRequest) returns (UploadResponse)
定义,其中
UploadRequest
包含
bytes chunk
int64 offset
string file_id
字段,而非整个文件塞进一个 message
服务端必须显式配置 Kestrel 的
MaxRequestBodySize = null
(不限制)和 gRPC 的
MaxReceiveMessageSize
(比如 100_000_000)
客户端不能用
ChannelCredentials.Insecure
走明文 HTTP/2(多数浏览器和工具不支持),必须配 TLS;本地开发可用自签名证书,但需
GrpcChannel
显式信任

.NET 6+ 的
IFormFile
在 HTTP/1.1 和 HTTP/2 下行为一致,但底层流不可 Seek

无论协议怎么变,ASP.NET Core 接收表单文件时都走

IFormFile
抽象,它背后是
ReadOnlyStream
(内存或临时磁盘流),
CanSeek
恒为
false
—— 这意味着你不能反复读取、不能直接传给需要
Seek()
的库(比如某些 ZIP 解压器)。

实操建议:

如果要校验哈希或多次解析内容,先用
file.CopyToAsync(memoryStream)
缓存到
MemoryStream
,再重置位置(
memoryStream.Position = 0
别在 Controller 里直接
await file.OpenReadStream().CopyToAsync(...)
后又想读一遍 —— 流已到底,下次读返回空
上传并发高时,
FormOptions.MultipartBodyLengthLimit
MemoryBufferThreshold
需权衡:设太低导致频繁落盘,太高则内存压力大

真正麻烦的从来不是选哪个协议,而是上传过程中网络抖动、服务重启、客户端切后台、磁盘满这些事 —— 协议只管“怎么送”,而健壮性得靠分片、校验、幂等、状态持久化一起兜底。

相关推荐