如何将JavaScript Blob发送至SignalR Hub?上传无数据问题求助
问题解决:SignalR Hub接收音频录制字节数组无数据的修复方案
核心问题分析
- 未初始化SignalR连接:代码仅创建了连接实例,但未执行
connection.start()完成连接建立,导致消息无法正常投递到Hub。 - 流式数据发送格式错误:直接将完整
ArrayBuffer传入subject.next(),SignalR无法将其解析为IAsyncEnumerable<byte>所需的字节序列,后端自然接收不到数据。
修复后的前端代码
const connection = new signalR.HubConnectionBuilder().withUrl("/hub").build(); // 先启动SignalR连接 (async () => { try { await connection.start(); console.log("SignalR连接已建立"); } catch (err) { console.error(err); } })(); let audioRecorder; let data = []; navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { audioRecorder = new MediaRecorder(stream); audioRecorder.addEventListener('dataavailable', e => { data.push(e.data); }); startButton.addEventListener('click', () => { // 每次录制前清空历史数据 data = []; audioRecorder.start(); }); stopButton.addEventListener('click', () => { audioRecorder.stop(); }); sendButton.addEventListener('click', async () => { if (data.length === 0) return; const blobObj = new Blob(data, { type: 'audio/webm' }); const arrayBuffer = await blobObj.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const subject = new signalR.Subject(); // 逐个发送字节,匹配后端IAsyncEnumerable<byte>格式 uint8Array.forEach(byte => subject.next(byte)); subject.complete(); try { await connection.invoke('upload', 'filename.webm', subject); console.log("音频数据发送成功"); } catch (err) { console.error(err); } }); })
后端代码优化(可选,针对大文件)
如果录制的音频文件较大,推荐按块传输以提升效率,修改后端参数为IAsyncEnumerable<byte[]>:
public partial class MyHub : Hub<IMyHub> { public async Task Upload(string filename, IAsyncEnumerable<byte[]> chunks) { using var memoryStream = new MemoryStream(); await foreach (var chunk in chunks) { await memoryStream.WriteAsync(chunk, 0, chunk.Length); } // 后续可添加保存文件/处理音频的逻辑 } }
对应前端分块发送逻辑(替换sendButton中的发送代码):
const blobObj = new Blob(data, { type: 'audio/webm' }); const arrayBuffer = await blobObj.arrayBuffer(); const chunkSize = 1024 * 16; // 16KB分块 const uint8Array = new Uint8Array(arrayBuffer); const subject = new signalR.Subject(); // 循环分块发送 for (let i = 0; i < uint8Array.length; i += chunkSize) { const chunk = uint8Array.slice(i, i + chunkSize); subject.next(chunk); } subject.complete(); await connection.invoke('upload', 'filename.webm', subject);
关键注意事项
- 必须确保SignalR连接成功启动后,再调用发送方法。
- 每次启动录制时清空
data数组,避免累积历史录制数据。 - 大文件场景下优先使用分块传输,降低内存占用和网络压力。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

