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

Blazor (Wasm)集成ReportViewer导出文件时HttpClient调用API不触发下载问题

问题根因

Blazor WebAssembly 运行在浏览器沙箱环境中,你现有代码中的File.WriteAllBytes是写入 WASM 运行时的虚拟内存文件系统,并不会触发浏览器的本地文件下载逻辑,因此没有下载提示弹出。

解决方案

这里提供两种常用实现方式,可根据业务场景选择:

方式1:JS互操作实现下载(适合接口需要自定义请求头/需要前端预处理文件内容的场景)

  1. 修改服务层的GetReport方法,不要直接写虚拟文件,返回文件核心信息即可,同时移除会导致死锁的.Result同步调用:
public async Task<(byte[] FileContent, string FileName, string MimeType)> GetReport(int reportType)
{
    var httpResponse = await _httpClient.GetAsync($"{_httpClient.BaseAddress}Report/GetReport/{reportType}");
    httpResponse.EnsureSuccessStatusCode();
    
    var content = await httpResponse.Content.ReadAsByteArrayAsync();
    var fileName = reportType == 1 ? "myPdf.pdf" : "myReport.xls";
    var mimeType = reportType == 1 ? "application/pdf" : "application/msexcel";
    
    return (content, fileName, mimeType);
}
  1. 在需要触发下载的Razor组件中注入IJSRuntime,并修改下载触发逻辑:
@inject IJSRuntime JSRuntime
@inject 你的服务类 _yourService

<!-- 下载按钮示例 -->
<button @onclick="()=>DownloadReport(1)" class="btn btn-outline-secondary">
    <span class="oi oi-data-transfer-download"></span>&nbsp;&nbsp;@(l.Keys["CPVH_Download_PDF"])
</button>

@code {
    private async Task DownloadReport(int reportType)
    {
        var (fileContent, fileName, mimeType) = await _yourService.GetReport(reportType);
        // 调用前端JS方法触发下载
        await JSRuntime.InvokeVoidAsync("downloadFile", fileName, mimeType, Convert.ToBase64String(fileContent));
    }
}
  1. 在wwwroot/index.html的body标签末尾添加下载JS方法:
<script>
function downloadFile(fileName, mimeType, base64Content) {
    const link = document.createElement('a');
    link.href = `data:${mimeType};base64,${base64Content}`;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
</script>

方式2:动态生成下载链接(更轻量,适合不需要预处理文件的场景)

不需要修改服务层,也不需要额外加全局JS,直接在组件中调用JS生成临时a标签触发下载即可,效果和你最开始直接写静态a标签完全一致:

@inject IJSRuntime JSRuntime
@inject HttpClient _httpClient

<button @onclick="()=>DownloadReport(1)" class="btn btn-outline-secondary">
    <span class="oi oi-data-transfer-download"></span>&nbsp;&nbsp;@(l.Keys["CPVH_Download_PDF"])
</button>

@code {
    private async Task DownloadReport(int reportType)
    {
        var downloadUrl = $"{_httpClient.BaseAddress}Report/GetReport/{reportType}";
        var fileName = reportType == 1 ? "myPdf.pdf" : "myReport.xls";
        await JSRuntime.InvokeVoidAsync("eval", 
            $@"var a = document.createElement('a');
               a.href='{downloadUrl}';
               a.download='{fileName}';
               document.body.appendChild(a);
               a.click();
               document.body.removeChild(a);");
    }
}

注意事项

如果你的接口需要携带JWT等自定义认证头,优先选择方式1,通过HttpClient发送请求时携带认证头,拿到文件内容后再触发下载;如果是Cookie认证的场景,两种方式都可以正常使用。

内容的提问来源于stack exchange,提问作者Zach Ioannou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:24:01