You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将JavaScript Blob发送至SignalR Hub?上传无数据问题求助

问题解决:SignalR Hub接收音频录制字节数组无数据的修复方案

核心问题分析

  1. 未初始化SignalR连接:代码仅创建了连接实例,但未执行connection.start()完成连接建立,导致消息无法正常投递到Hub。
  2. 流式数据发送格式错误:直接将完整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:03:32