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

如何在Blazor的非RAZOR类CS文件中访问Startup注册的Scoped服务数据

问题根因
  • Blazor Server 中 Scoped 服务的生命周期与用户连接的 Circuit 绑定,同一个用户会话内会复用同一个服务实例;而 MVC/API 控制器的 Scoped 服务生命周期为单次 HTTP 请求,每次请求都会创建全新的实例,二者属于完全隔离的作用域,所以控制器中注入的 DownloadingFile 和 Razor 页面中注入的不是同一个对象,读取值自然为 null。手动实例化的对象更是没有经过赋值,为空属于正常现象。
  • 如果是 Blazor WASM 项目,Scoped 服务完全运行在客户端浏览器中,服务端控制器不可能直接读取到客户端内存中的对象。
解决方案

方案1:不走控制器,直接用JS互操作在Blazor页面触发下载(最优解,无跨作用域问题)

无需额外开发控制器,直接通过JS在前端触发文件下载,适合文件体积不超过几百MB的场景:

  1. 在 wwwroot 目录的公共JS文件中添加下载方法:
function triggerDownload(fileName, base64Content) {
    const aTag = document.createElement('a');
    aTag.download = fileName;
    aTag.href = `data:application/octet-stream;base64,${base64Content}`;
    document.body.appendChild(aTag);
    aTag.click();
    document.body.removeChild(aTag);
}
  1. 在 Razor 页面中调用该方法:
@inject IJSRuntime JSRuntime
@* 页面其他内容 *@
@code {
    [Inject]
    private DownloadingFile DownloadFileObj { get; set; }
    private async Task Download()
    {
        // 确认theFile已赋值
        if(DownloadFileObj.theFile == null || DownloadFileObj.theFile.Length == 0) return;
        string base64Str = Convert.ToBase64String(DownloadFileObj.theFile);
        await JSRuntime.InvokeVoidAsync("triggerDownload", "自定义文件名.后缀", base64Str);
    }
}

方案2:通过缓存中转数据(需要保留控制器的场景)

用内存缓存作为中间层存储文件字节数组,关联唯一ID传递给控制器,控制器通过ID读取缓存内容返回:

  1. 先注册内存缓存服务(Program.cs/Startup.cs):
services.AddMemoryCache();
  1. 在 Razor 页面生成带唯一ID的下载链接:
@inject IMemoryCache MemoryCache
@* 页面其他内容 *@
@code {
    [Inject]
    private DownloadingFile DownloadFileObj { get; set; }
    private string DownloadLink { get; set; }
    private void GenerateDownloadLink()
    {
        // 赋值theFile后生成唯一ID
        string downloadId = Guid.NewGuid().ToString("N");
        // 缓存有效期设为10分钟,足够完成下载
        MemoryCache.Set(downloadId, DownloadFileObj.theFile, TimeSpan.FromMinutes(10));
        DownloadLink = $"/Download/Download?downloadId={downloadId}";
    }
}
  1. 调整控制器逻辑,从缓存读取数据:
public class DownloadController : Controller
{
    private readonly IMemoryCache _cache;
    public DownloadController(IMemoryCache cache)
    {
        _cache = cache;
    }
    public IActionResult Download(string downloadId)
    {
        if(!_cache.TryGetValue(downloadId, out byte[] fileBytes) || fileBytes == null)
        {
            return NotFound("下载链接已过期或无效");
        }
        // 可选:下载完成后立即删除缓存
        _cache.Remove(downloadId);
        return File(fileBytes, "application/octet-stream", "自定义文件名.后缀");
    }
}

如果是超大文件,可以将字节数组写入服务端临时目录,传递文件名/ID给控制器,控制器读取临时文件返回即可,注意定期清理临时目录的过期文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 00:27:03