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

ASP.NET Core转发AXIS摄像头multipart/x-mixed-replace流HttpClient阻塞求助

问题根因

你遇到的阻塞问题由两个核心错误共同导致:

  1. Blazor客户端的HttpClient默认GetStreamAsync方法实际使用HttpCompletionOption.ResponseContentRead行为,会等待完整响应加载完成才返回Task。但MJPG是无限长的实时流,永远不会加载完成,因此Task永远不会进入完成状态,代码就卡在await处。
  2. 你读取JPEG帧的逻辑错误,使用文本读取器StreamReader读取二进制JPEG数据,会导致数据损坏,就算过了GetStreamAsync的步骤也无法正常渲染图片。

修复步骤

第一步:替换GetStreamAsync为SendAsync,指定仅读取响应头就返回

手动构造请求,指定HttpCompletionOption.ResponseHeadersRead,强制HttpClient在读取完响应头后立刻返回,不缓冲响应正文:

private async void StartStreamingRequest(object sender, ElapsedEventArgs e) 
{
    Console.WriteLine("I'm about to request live stream");
    // 手动构造请求,指定响应头读取完成就结束等待
    using var request = new HttpRequestMessage(HttpMethod.Get, "CameraSystem/getStream");
    var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancellationTokenSource.Token);
    // 校验响应状态
    response.EnsureSuccessStatusCode();
    Stream responseStream = await response.Content.ReadAsStreamAsync(_cancellationTokenSource.Token);
    Console.WriteLine("Header found!");
    
    string boundary = "myboundary";
    var streamReader = new MultipartReader(boundary, responseStream);
    try
    {
        while (!_cancellationTokenSource.Token.IsCancellationRequested)
        {
            MultipartSection nextFrame = await streamReader.ReadNextSectionAsync(_cancellationTokenSource.Token);
            if (nextFrame == null) break; // 流断开时退出循环
            await DisplayOneFrameCallback(nextFrame.Body);
        }
    }
    catch (OperationCanceledException)
    {
        // 取消请求属于正常操作,无需额外处理
    }
}

第二步:修复JPEG帧读取逻辑,正确处理二进制数据

StreamReader是专门用于读取文本编码的工具,直接读取二进制JPEG数据会导致字节转码错误,必须直接读取字节数组再转base64:

private async Task DisplayOneFrameCallback(Stream body)
{
    // 直接读取二进制流为字节数组
    using var ms = new MemoryStream();
    await body.CopyToAsync(ms, _cancellationTokenSource.Token);
    byte[] frameBytes = ms.ToArray();
    _imgSrc = "data:image/jpeg;base64," + Convert.ToBase64String(frameBytes);
    StateHasChanged();
}

额外优化建议

  • 不要用Timer的Elapsed事件触发流请求,若Timer间隔小于流启动时间,会重复发起大量无效请求,建议仅在页面初始化时发起一次流请求即可。
  • 页面销毁时需调用_cancellationTokenSource.Cancel()主动释放流资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Alessandro Martinelli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 02:06:02