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

Blazor WebAssembly组件Dispose时img的HTTP流请求未中断问题求助

Blazor WebAssembly MJPG流请求导航后未终止问题

我有一个Blazor WebAssembly托管模式应用,包含如下组件:

liveStreaming.razor

@if (_isStreaming) {
    <img src="@_streamUrl">
} else {
    // show loading circle
}

liveStreaming.razor.cs

using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;
using System.Timers;

public partial class LiveStreaming: ComponentBase, IDisposable
{
    private bool _isStreaming;
    private string _streamUrl;
    private string _placeholderImgUrl;
    private Timer _checkConnectionTimer;

    protected override async Task OnInitializedAsync() {
        _isStreaming = false;
        _placeholderImgUrl = "emptyImage.jpg";
        _checkConnectionTimer = new Timer();
        _checkConnectionTimer.Interval = 6000;
        _checkConnectionTimer.Elapsed += CheckConnection;
        _checkConnectionTimer.Start();
        // [...]
    }

    private async void CheckConnection(object sender, ElapsedEventArgs e) {
        _checkConnectionTimer.Stop();
        if (IsConnectionEstablished()) {
            _isStreaming = true;
            _streamUrl = "http://192.168.0.2/axis-cgi/mjpg/video.cgi";
            StateHasChanged();
        } else {
            _isStreaming = false;
            StateHasChanged();
        }
        _checkConnectionTimer.Start();
    }

    public void Clean() {
        _checkConnectionTimer.Stop();
        _streamUrl = _placeholderImgUrl;
        StateHasChanged();
    }

    public async void Dispose() {
        if (_checkConnectionTimer != null) { _checkConnectionTimer.Dispose(); }
    }
}

问题现象

未调用Close方法时,若导航到Blazor应用的其他组件,即便Dispose方法已被触发,img标签发起的流请求也不会中断,可通过带宽占用观测到该问题。此外,若重新回到该页面,假设流带宽为3Mb/s,当前带宽占用会再叠加3Mb/s。每次离开再进入该页面都会出现该问题,就像HTTP流请求从未被中断,每次都会新建请求而旧请求始终保留。

为规避该问题,添加了上述Clean()方法,还搭建了一套复杂的组件切换机制:每次收到导航到其他组件的请求时,该机制会先调用当前加载组件的Clean()方法,再执行_navigationManager.NavigateTo("OtherComponentName"),即Clean()始终在Dispose()方法之前被调用。

对该方案并不满意,因为需要为理应默认支持的功能额外实现复杂机制。请问有没有更优的实现方式?

已验证的测试结果

  1. 将Clean()中的代码移到Dispose()中无任何效果,即便在StateHasChanged()后调用Task.Delay(1)也无效。推测是因为Dispose方法被调用后,组件就不再触发渲染了。
  2. 将Clean()中的代码修改为:
    _checkConnectionTimer.Stop();
    _isStreaming = false;
    StateHasChanged();
    
    也无任何效果。看起来必须修改img的src才能强制终止HTTP流请求。

补充发现

可以通过定义JavaScript方法负责重置img的src属性,并在Dispose()方法中调用该JS方法,即可规避该问题,无需使用上述的特殊 workaround。


解决方案

你补充的JS调用方案是目前最简洁的合规实现,核心原因是Blazor组件的Dispose触发时,组件渲染树已经被标记为废弃,此时调用StateHasChanged不会同步更新真实DOM,因此无法通过C#端修改变量的方式终止流请求。

方案1:轻量JS实现(推荐)

不需要额外的全局组件切换拦截机制,所有逻辑内聚在组件内部:

  1. 给img元素添加元素引用,注入JS运行时:
    @if (_isStreaming) {
        <img @ref="_streamImgRef" src="@_streamUrl">
    } else {
        // show loading circle
    }
    @inject IJSRuntime JSRuntime
    
  2. 后台代码声明引用变量,实现异步销毁逻辑:
    private ElementReference _streamImgRef;
    
    // 推荐用IAsyncDisposable替代IDisposable适配Blazor异步场景
    public async ValueTask DisposeAsync()
    {
        _checkConnectionTimer?.Dispose();
        if (_streamImgRef.Context != null)
        {
            // 直接修改DOM的src属性为空,触发浏览器主动终止流请求
            await JSRuntime.InvokeVoidAsync("eval", "arguments[0].src = ''", _streamImgRef);
        }
    }
    
    不需要单独定义全局JS方法,侵入性极低。

方案2:无JS实现

如果不想引入JS交互,可以通过监听导航事件提前执行清理:

[Inject]
private NavigationManager NavManager { get; set; }

protected override void OnInitialized()
{
    NavManager.LocationChanged += OnLocationChanged;
    // 原有初始化逻辑
}

private async void OnLocationChanged(object sender, LocationChangedEventArgs e)
{
    // 导航触发时、组件销毁前先执行清理,修改src触发DOM更新
    _checkConnectionTimer.Stop();
    _streamUrl = _placeholderImgUrl;
    _isStreaming = false;
    StateHasChanged();
    // 预留少量时间完成DOM更新
    await Task.Delay(50);
}

public void Dispose()
{
    NavManager.LocationChanged -= OnLocationChanged;
    _checkConnectionTimer?.Dispose();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:54:03