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

Blazor Server(.NET6)从Azure Blob下载11MB大文件报错

Blazor Server Azure Blob大文件下载超时问题解决

问题现状

基于.NET 6开发的Blazor Server项目,实现Azure Blob存储的文件上传下载功能时,小文件可正常下载,但下载11MB文件时耗时极长,最终抛出异常,浏览器控制台报错。

浏览器控制台错误截图:
浏览器控制台错误截图

当前实现逻辑:通过C#读取Azure Blob流,再通过DotNetStreamReference传递给JavaScript完成下载。

核心原因

  1. 重复读取Blob资源:原代码同时调用OpenReadAsync()和DownloadContentAsync(),两次获取Blob数据,浪费服务器资源且增加处理耗时。
  2. SignalR消息大小限制:Blazor Server依赖SignalR与前端通信,默认的SignalR消息大小限制无法容纳大文件流,直接通过DotNetStreamReference传递大文件会触发超时或大小超限错误,同时整个文件会被加载到服务器内存,引发性能问题。

解决方案

最优方案:直接返回FileResult(绕过JS互操作)

Blazor Server中下载大文件的最佳实践是直接返回文件流,让浏览器原生处理下载,彻底规避SignalR的消息限制和内存占用问题。

方式1:生成SAS链接直接跳转

在Razor页面中注入NavigationManager,生成Blob的临时SAS链接(私有Blob场景),引导浏览器跳转下载:

// Razor页面注入NavigationManager
[Inject]
public NavigationManager NavManager { get; set; }

private async Task DownloadBlob(string blobName)
{
    BlobContainerClient container = new BlobContainerClient(connectionString, "uploadfiles");
    try
    {
        BlobClient blobClient = container.GetBlobClient(blobName);
        if (await blobClient.ExistsAsync())
        {
            // 获取Blob的Content-Type
            var properties = await blobClient.GetPropertiesAsync();
            string contentType = properties.Value.ContentType;
            
            string downloadUrl;
            // 私有Blob生成临时SAS链接,有效期5分钟
            if (blobClient.CanGenerateSasUri)
            {
                var sasBuilder = new BlobSasBuilder(BlobContainerPermissions.Read, DateTimeOffset.UtcNow.AddMinutes(5))
                {
                    BlobName = blobName,
                    BlobContainerName = "uploadfiles"
                };
                downloadUrl = blobClient.GenerateSasUri(sasBuilder).ToString();
            }
            // 公开容器直接使用Blob的公开URL
            else
            {
                downloadUrl = blobClient.Uri.ToString();
            }
            
            // 跳转至下载链接,浏览器自动触发下载
            NavManager.NavigateTo(downloadUrl, true);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

方式2:通过API控制器返回流

创建API控制器直接返回Blob流,前端通过创建a标签触发下载:

// API控制器代码
[ApiController]
[Route("api/blob")]
public class BlobController : ControllerBase
{
    private readonly string _connectionString;

    public BlobController(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("AzureBlobStorage");
    }

    [HttpGet("download/{blobName}")]
    public async Task<IActionResult> Download(string blobName)
    {
        BlobContainerClient container = new BlobContainerClient(_connectionString, "uploadfiles");
        BlobClient blobClient = container.GetBlobClient(blobName);
        
        if (!await blobClient.ExistsAsync())
        {
            return NotFound();
        }
        
        var properties = await blobClient.GetPropertiesAsync();
        Stream stream = await blobClient.OpenReadAsync();
        
        return File(stream, properties.Value.ContentType, blobName);
    }
}

前端JavaScript代码:

window.downloadFromApi = (blobName) => {
    const anchor = document.createElement('a');
    anchor.href = `/api/blob/download/${blobName}`;
    anchor.download = blobName ?? '';
    anchor.click();
    anchor.remove();
}

备选方案:调整SignalR消息大小限制(不推荐)

如果必须保留JS互操作的方式,需修改SignalR配置增大消息限制,同时优化原代码移除重复读取:

  1. 修改Program.cs配置:
builder.Services.AddServerSideBlazor()
    .AddHubOptions(options =>
    {
        options.MaximumReceiveMessageSize = 50 * 1024 * 1024; // 设置为50MB
    });
  1. 优化后的Blazor页面代码:
BlobContainerClient container = new BlobContainerClient(connectionString, "uploadfiles");
try
{
    BlobClient blobClient = container.GetBlobClient(blobName);
    if (await blobClient.ExistsAsync())
    {
        var properties = await blobClient.GetPropertiesAsync();
        string contentType = properties.Value.ContentType;
        
        using Stream blobContent = await blobClient.OpenReadAsync();
        using var streamRef = new DotNetStreamReference(stream: blobContent);
        await JS.InvokeVoidAsync("downloadFileFromStream", blobName, streamRef);
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

注:此方式仍会将大文件加载到服务器内存,性能较差,仅适合无法使用直接下载方案的场景。

错误说明

浏览器控制台的错误源于SignalR默认消息大小限制,当传递的流数据超过限制时,SignalR连接会中断,进而抛出超时或消息过大的异常。

内容的提问来源于stack exchange,提问作者mnu-nasir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:05:32