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

Blazor WASM从WEB API下载超2GB大文件失败求助

大文件(>2GB)Blazor WASM下载失败问题解决

问题描述

服务端通过ASP.NET Web API返回ZIP文件,小文件(400-500MB)下载正常,但3GB文件下载时,Blazor WASM客户端调用ReadAsStreamAsync时抛出TypeError: Failed to fetch错误,无法完成流读取。

服务端代码:

return new FileStreamResult(new FileStream(Path.Combine(<path_to_file>), FileMode.Open), "application/zip") { FileDownloadName = generatedFileName };

客户端代码:

var request = new HttpRequestMessage(HttpMethod.Get, uri);                
request.Headers.Add("session", <session_data>);
// 其他headers
var result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
if (result.IsSuccessStatusCode)
{      
    Console.WriteLine("Getting stream...");
    var responseStream = await result.Content.ReadAsStreamAsync();
    Console.WriteLine("Stream done");
}

核心原因

  1. 浏览器Fetch API内存限制:Blazor WASM的HttpClient底层依赖浏览器Fetch API,默认会将整个响应加载到浏览器内存中。3GB文件远超浏览器可用内存上限,导致内存溢出,触发Failed to fetch错误。
  2. 非真正流式处理:ReadAsStreamAsync在浏览器环境下并非原生流式读取,而是先将响应缓存为Blob对象再转换为Stream,大文件无法完成缓存过程。
  3. 服务端配置缺失:可能未正确设置Content-Length头或启用分块传输,导致浏览器无法渐进式处理大文件;同时服务器(如IIS)可能限制了最大响应大小。

解决方案

1. 客户端改用浏览器原生下载(推荐)

绕过HttpClient的内存限制,直接调用浏览器原生下载能力,通过JS互操作实现:

步骤1:添加JS下载函数

在wwwroot目录创建downloadUtils.js:

window.downloadLargeFile = async function(url, sessionToken, fileName) {
    const response = await fetch(url, {
        method: 'GET',
        headers: {
            'session': sessionToken
            // 添加上其他需要的请求头
        }
    });

    if (!response.ok) {
        throw new Error(`下载失败,状态码:${response.status}`);
    }

    // 从响应头获取文件名(如果服务端返回)
    const contentDisposition = response.headers.get('Content-Disposition');
    if (contentDisposition && !fileName) {
        const matches = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
        if (matches != null && matches[1]) {
            fileName = decodeURIComponent(matches[1].replace(/['"]/g, ''));
        }
    }

    const blob = await response.blob();
    const blobUrl = URL.createObjectURL(blob);
    
    const a = document.createElement('a');
    a.href = blobUrl;
    a.download = fileName || 'download.zip';
    document.body.appendChild(a);
    a.click();
    
    // 清理资源
    document.body.removeChild(a);
    URL.revokeObjectURL(blobUrl);
};

步骤2:Blazor组件中调用JS函数

@inject IJSRuntime JS

// 在需要下载的方法中调用
await JS.InvokeVoidAsync("downloadLargeFile", uri, session_data, generatedFileName);

2. 服务端配置优化

  • 正确处理文件流:使用带缓冲和异步参数的FileStream,并启用分块传输或设置Content-Length:

    var filePath = Path.Combine(<path_to_file>);
    // 配置异步读取、共享权限、合理缓冲大小
    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 
                                    bufferSize: 4096, useAsync: true);
    var result = new FileStreamResult(fileStream, "application/zip") 
    { 
        FileDownloadName = generatedFileName 
    };
    // 可选:显式设置Content-Length(提前获取文件大小)
    result.ContentLength = new FileInfo(filePath).Length;
    return result;
    
  • 调整服务器响应大小限制:如果使用IIS,修改web.config增加最大允许响应大小:

    <system.webServer>
      <security>
        <requestFiltering>
          <!-- 设置为3GB(单位:字节) -->
          <requestLimits maxAllowedContentLength="3221225472" />
        </requestFiltering>
      </security>
    </system.webServer>
    

3. 客户端流式读取替代方案(复杂场景)

如果需要在客户端处理流而非直接下载,可通过JS互操作使用浏览器原生ReadableStream流式处理,避免加载整个文件到内存:

window.readLargeFileStream = async function(url, sessionToken, processChunk) {
    const response = await fetch(url, {
        headers: { 'session': sessionToken }
    });
    if (!response.ok) throw new Error('请求失败');
    
    const reader = response.body.getReader();
    
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        // 调用Blazor回调处理二进制chunk
        await processChunk(value);
    }
};

Blazor中调用并定义处理方法:

await JS.InvokeVoidAsync("readLargeFileStream", uri, session_data, 
    DotNetObjectReference.Create(this).InvokeAsync<bool>("ProcessChunk", value));

// 处理chunk的方法
[JSInvokable]
public async Task<bool> ProcessChunk(byte[] chunk)
{
    // 处理二进制数据,比如写入IndexedDB
    return true;
}

内容的提问来源于stack exchange,提问作者Attila Kiss

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:23:11