signalr 不适用于大文件传输,必须分块上传并校验哈希。应使用 http 接口上传分片,signalr 仅用于进度广播;服务端需缓冲请求、校验每块及全量 sha256,避免内存溢出与数据损坏。

SignalR 本身不支持大文件传输,别直接传 byte[]
SignalR 的 Hub 方法默认序列化走 JSON,且有默认消息大小限制(通常 32 KB)。直接把整个文件读成 byte[] 传给 hub.InvokeAsync("UploadFile", fileBytes) 会触发 InvalidOperationException: The message size exceeds the configured maximum 或直接超时。这不是配置调大就能解决的——JSON 序列化大数组开销高、内存暴涨、易被浏览器或代理截断。
- 必须分块(chunk)上传,每块控制在 64–256 KB,用
ArraySegment<byte></byte>或Memory<byte></byte>持有,避免重复拷贝 - 客户端需维护上传进度 ID(如 GUID),服务端用
ConcurrentDictionary<string memorystream></string>或临时文件暂存分块 - 最后一步由客户端发“完成”指令,服务端合并并保存到磁盘或 Blob 存储
前端用 fetch + ReadableStream 更稳,别依赖 HubConnection.invoke
SignalR 的 JS 客户端对二进制流支持弱,invoke 只适合小数据。大文件应绕过 Hub,改用普通 HTTP 接口上传,再通过 SignalR 广播状态(如 “上传中”、“已保存”、“校验失败”)。
- 后端暴露一个
POST /api/upload/{uploadId},接收multipart/form-data或原始application/octet-stream - 前端用
fetch分片上传,配合AbortController支持暂停/重试 - 上传过程中调用
connection.invoke("UpdateProgress", uploadId, progress)同步进度给其他客户端 - 避免在 Hub 方法里做 IO(如
File.WriteAllBytes),全部交给后台服务或托管服务处理
IFormFile 在 Controller 中接收分片,注意 Request.Body 流不可重复读
ASP.NET Core 默认将 Request.Body 缓存在内存或临时文件中,但若你手动调用 Request.Body.ReadAsync 一次后没重置位置(Seek),后续读取会返回空。尤其在用 IFormFile 时,它的 OpenReadStream() 返回的是新流,但底层 Request.Body 已被消耗。
- 确保
Startup.cs或Program.cs中启用缓冲:app.UseRequestBuffering()(.NET 6+) - 不要混合使用
IFormFile和直接读Request.Body;选一种方式到底 - 分片上传建议用裸流:
await request.Body.CopyToAsync(tempFileStream),比IFormFile更可控 - 记得检查
Content-Range头来定位当前块偏移,别依赖表单字段顺序
服务端合并分块前必须校验 SHA256,别只靠文件名或 ID
多个客户端可能用相同 uploadId 并发上传,或网络导致某块重复提交。仅靠内存字典或文件名拼接无法保证完整性。必须在客户端计算每块哈希,服务端验证并最终对完整文件再算一次总哈希。
- 客户端上传每块时附带
X-Chunk-Hash: sha256...头,服务端用SHA256.HashData(chunkBytes)校验 - 所有块接收完成后,从临时存储重新读取全量数据流,计算最终
SHA256,和客户端预提交的X-File-Hash对比 - 校验失败则删临时文件,返回 400,并要求客户端重传——别静默覆盖或写坏文件
- 临时文件路径别硬编码,用
Path.GetTempFileName()或注入IWebHostEnvironment.WebRootPath下的子目录










