Blazor Server 中用 FileStreamResult
或 FileContentResult
会失败
Blazor Server 是服务端渲染,但 HTTP 响应已由 SignalR 连接接管,直接返回
FileStreamResult或调用
Response.Body.WriteAsync不生效——浏览器收不到文件头,下载不会触发。你看到的可能是空白页、控制台报错
Failed to launch download: no file,或完全静默。
正确做法是:在后端提供一个标准 MVC/Controller 接口(非组件内方法),生成文件并返回
FileContentResult或
PhysicalFileResult,再从 Blazor 组件中用 JS 启动下载。 新建一个
Controllers/DownloadController.cs,添加
[HttpGet("api/download/{id}")] 方法
文件内容建议先写入 MemoryStream,再转为
byte[],避免流生命周期问题 设置
ContentType(如
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")和
FileDownloadName前端用
NavigationManager.NavigateTo("/api/download/123", forceLoad: true) 会刷新页面;更推荐用 JS interop 触发 window.location.href或
fetch+
URL.createObjectURL
Blazor WASM 中无法直接访问服务器文件系统
WASM 运行在浏览器沙箱里,
System.IO.File只能读取嵌入资源或通过
IJSRuntime拿到的用户选择文件。想“生成并下载”,所有逻辑必须在客户端完成。
典型场景:导出 CSV、生成 PDF(用
jsPDF)、导出 Excel(用
SheetJS)。关键点是别试图在 C# 里写文件到磁盘——没意义,也做不到。 用
MemoryStream构造数据(如 CSV 字符串转
Encoding.UTF8.GetBytes()) 调用 JS interop:
await JS.InvokeVoidAsync("downloadFile", fileName, base64String)
JS 端用 Uint8Array→
Blob→
URL.createObjectURL→
a.download触发保存 注意:WASM 下
Encoding.UTF8.GetBytes()中文不乱码,但若用
Encoding.Default可能出错
JS.InvokeVoidAsync("downloadFile", ...) 的 JS 实现要处理 Blob 兼容性
常见错误是只用
data:text/csv;base64,...链接,Safari 不支持长 URL,Edge 旧版可能截断。稳妥方式是走 Blob +
createObjectURL。
在
wwwroot/js/site.js里定义:
function downloadFile(fileName, base64String) {
const byteString = atob(base64String);
const len = byteString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = byteString.charCodeAt(i);
}
const blob = new Blob([bytes], { type: 'application/octet-stream' });
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);
}
不要省略 URL.revokeObjectURL,否则内存泄漏 如果文件类型明确(如 PDF),把
type改成
"application/pdf",有助于浏览器识别 IE11 不支持
Blob构造函数传
Uint8Array,需降级用
new Blob([bytes.buffer])
大文件下载时内存和超时要单独处理
生成 100MB Excel 或 ZIP 时,
MemoryStream会吃光 WASM 堆内存(默认 128MB),Server 端则可能触发 Kestrel 请求超时或 IIS 上传限制。 WASM:改用分块生成 + 流式下载不可行;实际应避免前端生成大文件,改由后端异步生成、返回下载链接(带 token 防盗链) Server:Controller 接口里别用
FileContentResult加载整个文件到内存;改用
FileStreamResult包裹
FileStream(注意
FileShare.Read和异步
CopyToAsync) 无论哪种模式,都应在响应头加
Content-Disposition: attachment; filename="xxx",且确保 Controller 方法不被
[ValidateAntiForgeryToken]拦截
最易被忽略的是:Blazor Server 的 SignalR 连接默认 5 分钟超时,而大文件生成可能耗时更久——得同时调大
HubOptions.KeepAliveInterval和客户端重连逻辑。
