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需权衡:设太低导致频繁落盘,太高则内存压力大
真正麻烦的从来不是选哪个协议,而是上传过程中网络抖动、服务重启、客户端切后台、磁盘满这些事 —— 协议只管“怎么送”,而健壮性得靠分片、校验、幂等、状态持久化一起兜底。
