今天老周要说的内容比较简单,所以大伙伴们不必紧张,能识字的都能学会。

在开始之前先来一段废话。

许多人都很关心,blazor 用起来如何?其实也没什么,做Web的无非就是后台代码+前台HTML(包含JS+CSS等)。Blazor 的初衷就是给咱们写C#的人用的,尽管不能完全代替 JS,但起码大多数情况下是可以的。某些特定情况下非用JS不可了,就使用.NET 与 JS 互操作就行了。不必大量使用,只在需要时用就行,不然会影响性能。这是什么样的场景呢?嗯,很熟悉的情场。

只要你以前写过 Windows Forms 窗体项目就懂了。这就跟.NET 调用 Win32 API 一样,大多数时候,你直接用.NET封装的类型就能搞定,但某些情况下你还得调用Win32 API,一样的道理。

虽然这几年,JS的语法也有所增强,也有TS的扩展,但写起来还是没有C#爽。这是照顾咱们大多数“全能程序猿”而推出的,有几家公司会专招一邦人来为你写前端(更别指望会给你招个妹子),这么人性化的公司可不多了。因此,Blazor 也不是什么高大上的神器,但可以为咱们这些“万能劳动力”减减压而已。

----------------------------------------------------------------------------------------------------------------------

老周今天说的是 Blazor 中的文件下载功能。其实,官方文档也给出了示例,你在开发过程完全可以照抄。抄代码也不是说一定是坏事,能够利用现有资源就尽情地用,不要犹豫。你不可能自己生产出汽车然后才开车的,不然汽车工厂干吗去?所以,以前有一位黑客级大神总结出:

1、能用 Excel 解决的问题你写个龟代码;

2、能用 PPT 解决的问题,你做啥视频特效;

3、别人都做出来的软件,你就用呗,何必自己造轮子;

4、借鉴(“抄”的雅称)别人的代码前最好先摸清楚人家的思路,大概弄懂是个啥原理再用。

其实,Blazor只不过把一些常用的JS实现的功能用C#替代而已,Web 应用的基本原理是不变的。也就是说,在Blazor应用中,做出文件下载功能的方法是很多滴。

官方示例的思路是:

A、服务器生成 Stream 对象;

B、对生成的.NET 流对象进行封送,传输到客户端(通过singalR),数据包装进 Blob 对象中;

C、互操作方式调用预先定义好的 JS 函数,提取 Blob 中的数据(模拟点击 document 生成的 <a>标签激活下载)。

不管是 blazor server 还是 blazor webassembly 原理一样。

老周补充一下这下方案,都是可行的。

A、写一个MVC控制器(其实理解为 API 控制器也一样,没有View罢了),返回文件内容,这个不难吧,然后在 Blazor 中只要利用一下指向此控制器的URL就行了,至于怎么做嘛,你喜欢咋弄都行;

B、原理和上面一样,只是不用写个MVC控制器,咱们何不发挥一下那个简练好用的 Mini-API 功能呢。

好了,前方精彩预警!

步骤1:我们建一个空白的 ASP.NET Core 应用项目。老周比较喜欢这个空白项目模板,灵活好用。ASP.NET Core 中所有技术都可以在同一个项目中融合使用。

步骤2:相信大家知道,C# 程序现在可以省略 Main 方法的定义,让编译器去生成默认代码。所以,ASP.NET Core 项目的代码比起过去版本一下子精简了很多。打开 Program.cs 文件(项目生成的是这名字,若你有强迫症,可以改名)。在调用 Build 方法之前,为应用程序注册以下服务。

var builder = WebApplication.CreateBuilder(args);
// 这些服务是必要的
builder.Services.AddServerSideBlazor();
// 我是图方便,让Razor页的目录直接设定于内容根目录
builder.Services.AddRazorPages().WithRazorPagesAtContentRoot();
var app = builder.Build();

Blazor 应用优先选用服务器端的,有特殊需求才考虑 Web Assembly。虽然不是什么硬规矩,但 Web 应用的优良传统都是服务器承担性能消耗,让客户端当上帝。故而,咱们要传承 Web 应用的奉献精神。

如果你刚接触 Blazor,可能会疑惑,为什么还要启用 Razor Pages 功能呢?因为 Blazor 也是Web应用是吧,它是在HTML页中加载的。嗯,你想一下,要是不先加载一个完整的HTML页,Blazor 怎么冒出来呢?所以,我们的应用程序要先加载一个“外壳”页,然后再通过它来加载 Blazor 应用。

从这个模式咱们就知道了,Blazor 应用其实是单个HTML页上的应用,Blazor 应用内的页面切换只是这个HTML页面内部一些标签的“轮换”罢了。即:Blazor 中的“页”本质上是一个HTML组件;而HTML组件就是把一堆HTML标签包起来,可以作为模板到处使用。这好比你的PC主机,有个机箱,把里面的主板、处理器、硬盘、内存、显卡什么的全部装好,当你要换个地方工作时,你只要搬动主机就行了,你不需要把内存、网卡的都拆出来又重新组装。

既然一个 Blazor 页是一个组件,那么,Blazor 应用在启动后,是不是应该要有一个“控制中心”,来操纵不同组件之间的切换?虽然普通的组件也能作为 Blazor 应用加载,但不能在多个组件中导航了。所以,我们要先编写这个“控制中心”,有了它,你就能到处穿越了,就像多拉B梦的时空门一样。

一般,我们把这个充当“主谋”的组件命名为 App,Razor 组件的文件扩展名是.razor。所以,文件名就是 App.razor。来,咱们动手写一下。

@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using System.Reflection <Router AppAssembly="typeof(Program).Assembly" Context="routedata">
<Found>
<RouteView RouteData="routedata" />
</Found>
<NotFound>
<p>应用程序挂了……</p>
</NotFound>
</Router>

前面的三个 @using 和 C# 中的 using 一个意思,引入咱们用到的命名空间。当然了,如果你不想在每个组件文件中都写一遍,还可以在 App.razor 同级目录下建一个名为 _Imports.razor 的文件(首字母可大写可小写),然后把 @using 写进去。

App 组件的根元素不是HTML元素,而是 Router 类,它可以根据应用内部的URL在不同组件间导航,客户端浏览器的地址栏不会变(前面说了,Blazor 是单页面的)。AppAssembly 属性指定 Blazor 组件要在哪个程序集中查找,99.9996% 情况下都是我们当前项目所在程序集。Context 是个很有意思的属性,它的功能是为当前元素(这里是Router)所关联的上下文件对象分配一个变量名,这个名字你可以随便取,这里我命名为“routedata”,如果不指定,默认名字是“context”。

这里头啥意思呢?原来啊,组件中呈现元素是用一个叫“帧”的玩意儿来表示的。对应两个委托类型:

delegate void RenderFragment(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
delegate Microsoft.AspNetCore.Components.RenderFragment RenderFragment<TValue>(TValue value)

注意到第二个委托有些意思,它返回了第一个委托类型的实例,但咱们最该关心的是它有个泛型参数 TValue,咱们上面所说的那个 Context 属性,所关联的上下文对象就是通过这个泛型参数来传递的。

传递上下文对象后能干些啥呢?还是以咱们这个 App 组件来举例。Router 接收到上下文对象(在运行的时候实际接收了被路由处理后的URL)后,Router 元素下面的子元素就可以访问这个上下文对象了,而访问方法就是引用 Context 属性分配的变量名(此处是 routedata)。

Router 元素必须包含两个子元素:

Found:如果从 AppAssembly 属性所指定的程序集中找到了与路由规则匹配的 Blazor 组件,那么,就把这个组件呈现在 RouteView 元素中;

NotFound:如果找不到匹配的组件,那就呈现它的子元素,这里是一个“屁”元素,文本是“应用程序挂了……”。

步骤3:建一个新 Blazor 组件,名为 Home.razor,作为此 Blazor 应用的真正主页。

@page "/"

<div>
<p>
下载文件:
</p>
<a href="/download" target="_blank">点这里</a>
</div>

作为 Blazor 的组件,要在首行明确标注 @page,“/”表示URL的根路径,即默认打开的“页面”。

为了简单演示,此处<a>元素指向了下载文件的地址,点一下就开始下载。/download 指向一个 Mini-API,这个咱们到最后再写。

步骤4:Blazor 组件完工了,接下来要弄一个 Razor 页,它是一个完整的HTML文档,用来加载 Blazor 应用。命名为 appLoader.cshtml。注意,文件扩展名不同,不是 Razor 组件。

@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<base href="~/" />
</head>
<body>
@*相关脚本*@
<script src="_framework/blazor.server.js"></script>
@*加载启动组件*@
<component type="typeof(XXX.App)" render-mode="ServerPrerendered" />
</body>
</html>

作为 Razor Page ,你懂的,首行要注明 @page,第二行是标记要使用 Tag Helper(标记帮助器)。因为稍后咱们要用 component 元素来加载 App 组件。

XXX是你那个 App 组件所在的命名空间。有个重要的 JS 脚本—— blazor.server.js,绝对不能忘了,否则客户端无法启动 Blazor 专用的 singnalR 连接。这个脚本不在我们项目中,而包装在.NET 类库中,所以我们不用管它,记得引用就行。

步骤5:最后,咱们补全 Program.cs 中的代码。

// Blazor需要静文件的访问
app.UseStaticFiles();
app.UseRouting();
// 此处比5.0简练,不必通过Endpoint来添加映射
app.MapBlazorHub();
// blazor app 第一次访问时,应用尚未加载,会404的
// 所以要先访问一下某个page,让这个page去加载app
app.MapFallbackToPage("/appLoader"); app.Run();

虽然咱们这项目中没有 wwwroot 中的静态资源,但JS要加载 blazor.server.js,获取这个脚本需要静态文件功能来支持。

MapBlazorHub 方法要记得调用,否则客户端进来的 HTTP 请求无法由 Blazor 类库来处理。

最下面一句 MapFallbackToPage 也很重要。前面咱们分析过,Blazor 应用需要一个完整的 HTML 页面来加载,所以,当客户端首次访问根 URL(或其他组件URL)时,由于 Blazor 未启动,组件无法加载。

所以,当首次访问失败时转到 /appLoader 来加载并启动 Blazor 应用。

步骤6:实现下载文件的 Mini-API。

app.MapGet("/download", () =>
{
// 随机弄些玩意儿
byte[] data = null;
string txt = "床前明月光\n有逼就能装\n手持玩具枪\n喝辣又吃香";
data = System.Text.Encoding.UTF8.GetBytes(txt);
return Results.File(data, "application/octet-stream", "abc.txt");
});

Program.cs 完整代码如下:

var builder = WebApplication.CreateBuilder(args);
// 这些服务是必要的
builder.Services.AddServerSideBlazor();
// 我是图方便,让Razor页的目录直接设定于内容根目录
builder.Services.AddRazorPages().WithRazorPagesAtContentRoot();
var app = builder.Build(); // Mini-API,简单文件下载
app.MapGet("/download", () =>
{
……
}); // Blazor需要静文件的访问
app.UseStaticFiles();
app.UseRouting();
// 此处比5.0简练,不必通过Endpoint来添加映射
app.MapBlazorHub();
// blazor app 第一次访问时,应用尚未加载,会404的
// 所以要先访问一下某个page,让这个page去加载app
app.MapFallbackToPage("/appLoader"); app.Run();

运行起来,测测效果。

点一下页面上的链接,嗯,Perfect !

记事本打开看看下载的文件。

当然了,你也可以像官方示例那样,用 JS 动态创建个<a>标签,然后模拟 Click。

来,咱们改一下。

在项目中新建一个目录,命名为 wwwroot,然后在wwwroot下建一个脚本文件,命名为 test.js。用JS写个函数。

function demoDown() {
// 动态创建元素
var ele = document.createElement("a");
// 设置下载URL
ele.href = '/download';
ele.target = '_blank';
// 模拟点击
ele.click();
ele.remove(); //没有利用价值了,杀!
}

待会儿,我们得用互操作来调用这个JS函数。

打开 appLoader.cshtml,改一下HTML,引用 test.js。

    <body>
@*相关脚本*@
<script src="_framework/blazor.server.js"></script>
<script src="~/test.js"></script>
@*加载启动组件*@
<component type="typeof(SuatApp.App)" render-mode="ServerPrerendered" />
</body>

再打开 Home.razor 组件,改一下,把 a 元素改成 button。

@page "/"
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@inject IJSRuntime JS
<div>
<p>
下载文件:
</p>
<button @onclick="OnClick">点这里领取美人一名</button>
</div> @code {
private async Task OnClick()
{
// 互操作,调用JS函数
await JS.InvokeVoidAsync("demoDown");
}
}

@inject 用来获取依赖注入的 JsRuntime 对象,在 OnClick 方法中用它来调用JS函数。被调用的 JS 函数就是我们刚刚写的 demoDown。

可以了,再次运行,看效果。

然后点一下页面上那个充满诱惑的按钮,下载文件。

好了,这样弄基本咱们日常开发需求了。

【ASP.NET Core】Blazor+MiniAPI完成文件下载的更多相关文章

  1. ASP.NET Core Blazor 用Inspinia静态页模板搭建简易后台(实现菜单选中)

    Blazor 是一个用于使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建丰富的交互式 UI. 共享使用 .NET 编写的服务器端和客户端应用逻辑 ...

  2. ASP.NET Core Blazor 初探之 Blazor Server

    上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly).这次来看看Blazor Server该怎么玩. ...

  3. [Asp.Net Core] Blazor Server Side 扩展用途 - 配合CEF来制作带浏览器核心的客户端软件 (二) 可运行版本

    前言 大概3个星期之前立项, 要做一个 CEF+Blazor+WinForms 三合一到同一个进程的客户端模板. 这个东西在五一的时候做出了原型, 然后慢慢修正, 在5天之前就上传到github了. ...

  4. ASP.NET Core Blazor Webassembly 之 组件

    关于组件 现在前端几大轮子全面组件化.组件让我们可以对常用的功能进行封装,以便复用.组件这东西对于搞.NET的同学其实并不陌生,以前ASP.NET WebForm的用户控件其实也是一种组件.它封装ht ...

  5. ASP.NET Core Blazor Webassembly 之 数据绑定

    上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件).这次继续学习Blazor的数据绑定相关的知识.当代前端框架都离不开数据绑定技术. ...

  6. ASP.NET Core Blazor Webassembly 之 路由

    web最精妙的设计就是通过url把多个页面串联起来,并且可以互相跳转.我们开发系统的时候总是需要使用路由来实现页面间的跳转.传统的web开发主要是使用a标签或者是服务端redirect来跳转.那今天来 ...

  7. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(上)

    学习ASP.NET Core Blazor编程系列一--综述 一.概述 Blazor 是一个生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 U ...

  8. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...

  9. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(下)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  10. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(完)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

随机推荐

  1. java配置文件的使用 —— 设置一个类为单例模式

    阅读本文章前建议先阅读:java通过JDBC访问sqlserver数据库 一.使用原因:通过JDBC连接数据库时有时会需要连接不同的数据库,而jar包.连接url.用户名和密码等都是写定在程序中,不便 ...

  2. 莫烦python教程学习笔记——使用波士顿数据集、生成用于回归的数据集

    # View more python learning tutorial on my Youtube and Youku channel!!! # Youtube video tutorial: ht ...

  3. Abp Vnext 替换Redis的实现为csredis

    Host项目安装csredis的nuget包和data protect包并 移除 Microsoft.Extensions.Caching.StackExchangeRedis Microsoft.A ...

  4. react原理分析--this.state修改引起的重新渲染

    整理向,非原创,目的是整理出浅显易懂的方向性说明. 比如现有 this.state={name:"小明",age:18} 我们说修改组件的状态要用this.setState()来实 ...

  5. 2020KCTF秋季赛签到题

    比赛平台:https://ctf.pediy.com/game-season_fight-158.htm 开场 签到题 例行检查,64位程序,无壳 试运行一下,看看大概的情况 64位ida载入,根据运 ...

  6. 删除…Remove…(Power Query 之 M 语言)

    删除行(表): 删除指定行:=Table.RemoveRows( 表, 起始行数, 删除的行数) 起始行数从0开始计 删除前面N-.Skip/RemoveFirstN 删除后面N-.RemoveLas ...

  7. CF508A Pasha and Pixels 题解

    Content 有一个 \(n\times m\) 的矩阵,一开始全部格子被染成白色. 接下来有 \(k\) 个操作,每一个操作表示把一个格子染成黑色.问第一次出现 \(2\times 2\) 的全部 ...

  8. LuoguP7106 双生独白 题解

    Content 给定一个 十六进制颜色码(一个长度为 \(7\) 的字符串,意义详见题面),请输出其反色的十六进制颜色码. 数据范围:颜色的 R,G,B 值保证在 \(255\) 以内. Soluti ...

  9. atexit模块介绍

    atexit 模块介绍 python atexit 模块定义了一个 register 函数,用于在 python 解释器中注册一个退出函数,这个函数在解释器正常终止时自动执行,一般用来做一些资源清理的 ...

  10. uniapp+nvue实现仿微信App界面+功能 —— uni-app实现聊天+语音+视频+图片消息

    基于uniapp + nvue实现的uniapp仿微信界面功能聊天应用 txim 实例项目,实现了以下功能. 1: 聊天会话管理 2: 好友列表 3: 文字.语音.视频.表情.位置等聊天消息收发 4: ...