C# SignalR文件传输 C#如何利用SignalR实现客户端与服务器的文件交换

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

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
下的子目录
SignalR 是实时通信的胶水,不是文件传输协议。真正卡点永远在流控制、内存管理、并发安全和端到端校验——这些细节没压住,光连上 Hub 没用。

相关推荐

热文推荐