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

Blazor Server端大文件带进度下载及多文件同时下载问题咨询

Blazor Server 多文件带进度下载解决方案

问题根因梳理

  • 第一种方案:全量读取文件字节通过JSInterop传输,浏览器感知不到传输过程,因此无原生下载进度,且大文件会大量占用服务端和客户端内存,本身就不适用于大文件场景
  • 第二种方案:Http.GetAsync是服务端发起的HTTP请求,和客户端浏览器完全无关,自然不会触发浏览器的下载行为
  • 第三种方案:循环调用open打开新标签会触发浏览器的弹窗拦截规则,因此仅第一个文件能正常下载

具体实现方案

1. 优化后端下载接口

首先修改控制器代码,避免全量读取文件到内存,改用流式返回,降低服务端内存消耗:

[HttpGet("download/model/{JobId}/{fileName}")]
public IActionResult DownloadFile([FromRoute] string JobId, [FromRoute] string fileName) 
{
    if (string.IsNullOrEmpty(JobId) || string.IsNullOrEmpty(fileName)) 
    {
        return BadRequest();
    }
    var FolderPath = $"xxxx";
    var filePath = Path.Combine(environment.WebRootPath, FolderPath, fileName);
    if (!System.IO.File.Exists(filePath))
    {
        return NotFound();
    }
    // 直接返回文件流,不读取全量字节
    return PhysicalFile(filePath, "application/octet-stream", fileName);
}

2. 新增批量下载JS方法

在wwwroot/js目录下新增下载工具方法,通过动态生成a标签触发下载,不会触发弹窗拦截:

window.batchDownloadFiles = function (downloadUrls) {
    downloadUrls.forEach(url => {
        const a = document.createElement('a');
        a.href = url;
        a.download = ''; // 让浏览器使用接口返回的文件名
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    });
}

记得在_Host.cshtml中引入该JS文件。

3. Blazor端调用代码

修改下载方法,调用上面的JS方法批量传入所有下载地址:

public async Task DownloadFiles() 
{
    // 构造所有下载地址
    var downloadUrls = selectedFiles.Select(file => $"Download/Model/{JobId}/{file.Name}").ToList();
    // 调用JS批量下载
    await JSRuntime.InvokeVoidAsync("batchDownloadFiles", downloadUrls);
}

自定义页面内下载进度(可选)

如果需要在Blazor页面内显示每个文件的下载进度,而不是依赖浏览器自带的下载栏进度,可以用JS的fetch API读取流进度,回调到Blazor更新UI:

  1. 新增JS方法:
window.downloadFileWithProgress = async function (dotNetRef, url, fileIndex) {
    const response = await fetch(url);
    const total = response.headers.get('content-length');
    const reader = response.body.getReader();
    let received = 0;
    const chunks = [];
    while(true) {
        const {done, value} = await reader.read();
        if (done) break;
        chunks.push(value);
        received += value.length;
        const progress = Math.round((received / total) * 100);
        // 回调Blazor方法更新进度
        await dotNetRef.invokeMethodAsync('UpdateDownloadProgress', fileIndex, progress);
    }
    // 下载完成触发保存
    const blob = new Blob(chunks);
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    const contentDisposition = response.headers.get('content-disposition');
    const fileName = contentDisposition.match(/filename="?([^"]+)"?/)[1];
    a.download = fileName;
    a.click();
    URL.revokeObjectURL(a.href);
}
  1. Blazor端代码:
// 定义进度存储属性
public List<int> DownloadProgress { get; set; } = new();
[JSInvokable]
public void UpdateDownloadProgress(int fileIndex, int progress)
{
    DownloadProgress[fileIndex] = progress;
    StateHasChanged();
}
public async Task DownloadFilesWithCustomProgress()
{
    DownloadProgress.Clear();
    var dotNetRef = DotNetObjectReference.Create(this);
    for (var i = 0; i < selectedFiles.Count; i++)
    {
        DownloadProgress.Add(0);
        var url = $"Download/Model/{JobId}/{selectedFiles[i].Name}";
        // 需要并发下载可以去掉当前await,按实际需求调整
        await JSRuntime.InvokeVoidAsync("downloadFileWithProgress", dotNetRef, url, i);
    }
}

内容的提问来源于stack exchange,提问作者Sonny Hansen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 04:15:02