postasync 不能用于大文件上传,因其不支持分片、无进度跟踪、无法断点续传;根本原因包括默认超时过短、整文件内存加载易oom、http协议缺乏续传机制。

直接用 HttpClient.PostAsync 传大文件必然失败,不是配置问题,是设计缺陷——它不支持分片、不维护进度、不处理中断恢复。
为什么 PostAsync 不能用于大文件上传
常见现象:上传卡在 70%~90%,抛 TaskCanceledException 或 HttpRequestException;服务端日志显示请求超时或内存溢出。根本原因有三个:
-
HttpClient默认Timeout是 100 秒,大文件远超此限 - 若用
File.ReadAllBytes()或IFormFile.OpenReadStream().ToArray(),整块加载进内存,OutOfMemoryException很快出现 - HTTP 协议本身不携带“已传多少”信息,服务端无法判断是否续传,客户端也无法跳过已成功部分
ASP.NET Core 后端如何安全接收单个分片
关键不是“存到哪”,而是“怎么避免并发写崩、覆盖、丢数据”。别用 Session 存分片状态——进程重启就全丢。必须用 Redis 或数据库持久化 fileId → chunkIndex → status 映射。
- 分片文件名必须含
fileId和chunkIndex,例如uploads/{fileId}/{chunkIndex}.bin,禁止拼接为同一文件名反复写入 - 写入时用
FileMode.Create+FileShare.None,防止多线程/多请求同时写同一块 - 用
FileStream流式拷贝,缓冲区设为4096,禁用CopyTo的默认 81920 缓冲(太大易阻塞) - 校验不能依赖
IFormFile.Length——它可能被伪造或截断,应以实际写入字节数为准,或额外计算SHA256比对
C# 客户端如何实现真正可恢复的断点续传
核心动作不在发请求,而在“查状态 → 跳已传 → 补失败 → 记本地”。客户端必须自己管理进度,HttpClient 不会记住任何东西。
- 上传前先 GET
/upload/status?fileId={fileId},拿到服务端已确认成功的chunkIndex列表,而不是猜“上次传到第几片” - 本地进度必须落盘,推荐用行式文本(如每行一个
chunkIndex),用File.AppendAllText()追加,避免并发写损坏 JSON 文件 - 每个分片请求要独立设置
Timeout(如TimeSpan.FromMinutes(5)),不能复用全局HttpClient.Timeout - 不要用单例
HttpClient发上百个分片请求——Windows 默认MaxConnectionsPerServer=10,很快卡死;建议每任务新建实例,或配SocketsHttpHandler { MaxConnectionsPerServer = 32 }
合并前最容易被忽略的校验环节
所有分片收齐后,服务端不能直接按序拼接就完事。漏掉这步,用户传完发现文件打不开,问题根本没法回溯。
- 必须重新计算最终文件的
SHA256,与客户端初始化时上报的fileHash对比;不一致就返回400 Bad Request并清空临时目录 - 合并过程要用原子操作:先写入
uploads/{fileId}/merged.tmp,完整写完再File.Move替换正式文件,防止读取时遇到半成品 - 临时目录需定期清理(如 24 小时未完成的
fileId自动过期),否则磁盘迟早被占满











