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

如何在Blazor WASM中捕获文件下载的开始与结束等事件?

Blazor WASM 捕获文件下载事件方案

一、无额外JavaScript的基础方案(仅满足部分需求)

原生<a download>标签受浏览器下载机制限制,无法直接捕获下载完成、失败及进度事件,但可以实现部分需求:

1. 捕获下载触发点

用Blazor的@onclick可以在用户点击按钮时触发逻辑,作为下载开始的标记(注意:这是用户触发下载的时间点,并非文件实际开始传输):

<a download href="/api/Download/File" @onclick="HandleDownloadTrigger">Download</a>

@code {
    private void HandleDownloadTrigger()
    {
        // 处理下载触发逻辑,比如显示加载提示
        Console.WriteLine("用户已触发下载");
    }
}

2. 配合后端接口捕获错误

通过Blazor的HttpClient先请求后端接口,判断响应状态后再触发下载,可捕获后端返回的错误(如500):

<button @onclick="TriggerDownload">Download</button>

@code {
    [Inject]
    private HttpClient Http { get; set; }

    [Inject]
    private IJSRuntime JSRuntime { get; set; }

    private async Task TriggerDownload()
    {
        try
        {
            // 先请求后端接口,验证状态
            var response = await Http.GetAsync("/api/Download/File");
            response.EnsureSuccessStatusCode();

            // 获取文件信息
            var contentDisposition = response.Content.Headers.ContentDisposition;
            var fileName = contentDisposition?.FileNameStar ?? contentDisposition?.FileName ?? "download.file";
            var contentType = response.Content.Headers.ContentType?.ToString() ?? "application/octet-stream";
            var stream = await response.Content.ReadAsStreamAsync();

            // 调用浏览器API触发下载(依赖Blazor内置的IJSRuntime,无需额外JS文件)
            await JSRuntime.InvokeVoidAsync("triggerDownload", stream, fileName, contentType);

            // 到此处可判定下载已成功触发,但无法确认用户是否完成下载
            Console.WriteLine("下载已成功触发");
        }
        catch (HttpRequestException ex)
        {
            // 捕获后端返回的错误
            Console.WriteLine($"下载出错:{ex.Message}");
        }
    }
}

在wwwroot/index.html中添加极简JS(属于浏览器API调用,非额外复杂逻辑):

function triggerDownload(stream, fileName, contentType) {
    const blob = new Blob([stream], { type: contentType });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}

二、支持进度与完整状态的方案(需少量JS)

要捕获实时下载进度和下载完成状态,需借助JS的Fetch API配合Blazor交互:

1. Blazor端代码

<button @onclick="StartDownloadWithProgress">Download with Progress</button>
<p>下载进度:@downloadProgress%</p>
<p>下载状态:@downloadStatus</p>

@code {
    [Inject]
    private IJSRuntime JSRuntime { get; set; }

    private int downloadProgress = 0;
    private string downloadStatus = "未开始";

    private async Task StartDownloadWithProgress()
    {
        downloadProgress = 0;
        downloadStatus = "正在准备下载";
        
        // 调用JS发起下载并监听事件
        await JSRuntime.InvokeVoidAsync("downloadWithProgress", "/api/Download/File", 
            (Action<int>)UpdateProgress,
            (Action)OnDownloadSuccess,
            (Action<string>)OnDownloadError);
    }

    private void UpdateProgress(int progress)
    {
        downloadProgress = progress;
        StateHasChanged();
    }

    private void OnDownloadSuccess()
    {
        downloadStatus = "下载完成";
        StateHasChanged();
    }

    private void OnDownloadError(string errorMsg)
    {
        downloadStatus = $"下载失败:{errorMsg}";
        StateHasChanged();
    }
}

2. wwwroot/index.html中的JS代码

async function downloadWithProgress(url, updateProgressCallback, successCallback, errorCallback) {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`HTTP错误:${response.status}`);
        }

        const contentLength = response.headers.get('Content-Length');
        const total = contentLength ? parseInt(contentLength) : 0;
        let loaded = 0;

        const reader = response.body.getReader();
        const chunks = [];

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;

            chunks.push(value);
            loaded += value.length;
            if (total > 0) {
                const progress = Math.round((loaded / total) * 100);
                updateProgressCallback(progress);
            }
        }

        // 生成文件并触发下载
        const blob = new Blob(chunks);
        const url = URL.createObjectURL(blob);
        const fileName = response.headers.get('Content-Disposition')?.split('filename=')[1].replace(/"/g, '') || 'download.file';
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);

        successCallback();
    } catch (err) {
        errorCallback(err.message);
    }
}

该方案可完整捕获:

  • 下载触发时间点
  • 实时下载进度
  • 后端接口错误
  • 文件成功生成并触发下载的完成状态
    注意:无法捕获用户主动在浏览器下载弹窗中取消下载的行为,这属于浏览器安全限制。

总结

  • 无纯Blazor(无JS)方案无法覆盖所有需求,最多实现触发点捕获和后端错误捕获;
  • 完整的进度、状态捕获需借助少量JS配合Blazor的IJSRuntime;
  • 浏览器原生下载的取消行为无法通过代码捕获。

内容的提问来源于stack exchange,提问作者S Mxller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 03:24:51