http/1.1大文件上传卡在100-continue因expect头未获服务端及时响应,nginx/iis等代理常忽略该机制;应禁用expect100continue、调大各层请求体限制,并采用分片+幂等+断点续传保障健壮性。

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










