如何在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
相关产品推荐
相关产品推荐

