通过HTTP请求获取的Web资源很多都来源于存储在服务器磁盘上的静态文件。对于ASP.NET应用来说,如果将静态文件存储到约定的目录下,绝大部分文件类型都是可以通过Web的形式对外发布的。“Microsoft.AspNetCore.StaticFiles” 这个NuGet包中提供了三个用来处理静态文件请求的中间件,我们可以用它们搭建一个文件服务器。(本篇提供的实例已经汇总到《ASP.NET Core 6框架揭秘-实例演示版》)

[1901]以Web形式发布文件(图片)(源代码

[1902]以Web形式发布文件(PDF)(源代码

[1903]显式文件目录结构(源代码

[1904]显示目录的默认页面(源代码

[1905]定制目录的默认页面(源代码

[1906]设置默认的媒体类型(源代码

[1907]映射文件扩展名的媒体类型(源代码

[1901]以Web形式发布文件(图片)

作为演示实例是ASP.NET应用具有如图1所示的项目结构。在默认作为WebRoot的“wwwroot”目录下,我们将JavaScript脚本文件、CSS样式文件和图片文件存放到对应的子目录(js、css和img)下。该目录下的所有文件将自动发布为Web资源,客户端可以访问相应的URL来读取对应它们的内容。

图1 静态文件发布的项目结构

针对具体某个静态文件的请求是通过StaticFileMiddleware中间件来处理。如下所示的演示程序中调用IApplicationBuilder接口的UseStaticFiles扩展方法注册的就是这个中间件。

var app = WebApplication.Create();
app.UseStaticFiles();
app.Run();

演示程序运行之后,就可以通过GET请求的方式来读取对应文件的内容,目标文件相对于WebRoot目录的路径就是对应URL的路径,如JPG图片文件“~/wwwroot/img/dolphin1.jpg”对应的URL路径为“/img/dolphin1.jpg”。如果直接利用浏览器访问这个URL,目标图片就会直接以图2所示的形式显示出来。

图2 以Web形式请求发布的图片文件

[1902]以Web形式发布文件(PDF)

上面通过一个简单的实例将WebRoot所在目录下的所有静态文件发布为Web资源,如果需要发布的静态文件存储在其他目录下呢?比如我们将上面演示的应用程序的一些文档存储在图3所示的“~/doc/”目录下,那么对应的程序又该如何编写呢?

图3 发布“~/doc/”和“~/wwwroot”目录下的文件

ASP.NET应用在大部分情况下都是利用一个IFileProvider对象来读取文件的,针对静态文件的读取请求处理也不例外。StaticFileMiddleware中间件内部维护着一个IFileProvider对象和请求路径的映射关系。如果调用UseStaticFiles方法没有指定任何参数,那么这个映射的路径就是应用的基地址(PathBase),采用的IFileProvider对象就是指向WebRoot目录的PhysicalFileProvider对象。上述需求可以通过定制这个映射关系来实现。如下面的代码片段所示,我们在现有程序的基础上额外添加了一次针对UseStaticFiles扩展方法的调用,并利用作为参数的StaticFileOptions配置选项添加请求路径(“/documents”)与对应IFileProvider对象(针对路径“~/doc/”的PhysicalFileProvider对象)之间的映射关系。

using Microsoft.Extensions.FileProviders;

var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var options = new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(path),
RequestPath = "/documents"
}; var app = WebApplication.Create();
app
.UseStaticFiles()
.UseStaticFiles(options);
app.Run();

按照上面这段程序指定的映射关系,对于存储在“~/doc/”目录下的这个PDF文件(checklist.pdf),请求URL采用的路径就应该是“/documents/checklist.pdf”。如果利用浏览器请求这个地址时,PDF文件的内容就会按照图4所示的形式显示在浏览器上。

图4 以Web形式请求发布的PDF文件

[1903]显式文件目录结构

StaticFileMiddleware中间件只会处理针对具体的某个静态文件的请求,如果利用浏览器发送一个针对目录路径的请求(比如“/img”),我们将得到状态为“404 Not Found”的响应。如果希望浏览器呈现出目标目录的结构,就可以注册DirectoryBrowserMiddleware中间件。这个中间件会返回一个HTML页面,请求目录下的结构会以表格的形式显示在这个页面中。我们演示的程序按照如下方式调用IApplicationBuilder接口的UseDirectoryBrowser扩展方法注册了这个中间件。

using Microsoft.Extensions.FileProviders;

var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; var app = WebApplication.Create();
app
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions); app.Run();

当上面的应用启动之后,如果利用浏览器向针对某个目录的URL(如“/”或者“/img”)发起请求,目标目录的内容(包括子目录和文件)就会以图5所示的形式显示在一个表格中。可以看出在呈现的表格中,当前目录的子目录和文件均会显示为链接。

图5 显示目录内容

[1904]显示目录的默认页面

UseDirectoryBrowser中间件会将整个目标目录的结构和所有文件全部暴露出来,所以这个中间件需要根据自身的安全策略谨慎使用。对于针对目录的请求,更加常用的处理策略就是显示一个保存该目录下的默认页面。默认页面文件一般采用如下四种命名约定(default.htm、default.html、index.htm和index.html)。默认页面的呈现实现DefaultFilesMiddleware中间件中,我们演示的这个应用可以按照如下方式调用IApplicationBuilder接口的UseDefaultFiles扩展方法来注册这个中间件。

using Microsoft.Extensions.FileProviders;

var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; var app = WebApplication.Create();
app
.UseDefaultFiles()
.UseDefaultFiles(defaultOptions)
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions); app.Run();

下面在“~/wwwroot/img/”和“~/doc”目录下分别创建一个名为index.html的默认页面,并且在该.html文件的主体部分指定一段简短的文字(This is an index page!)。我们在应用启动之后利用浏览器访问这两个目录(“/img”和“/documents”),默认页面就会以图6的形式显示出来。

图6 显示默认页面

[1905]定制目录的默认页面

我们须将DefaultFilesMiddleware中间件放在StaticFileMiddleware和DirectoryBrowserMiddleware中间件之前。这是因为DirectoryBrowserMiddleware和DefaultFilesMiddleware中间件处理的均是针对目录的请求,如果先注册DirectoryBrowserMiddleware中间件,那么显示的总是目录的结构。如果先注册用于显示默认页面的DefaultFilesMiddleware中间件,那么在默认页面不存在的情况下它会将请求分发给后续中间件,此时DirectoryBrowserMiddleware中间件将当前目录的结构呈现出来。要先于StaticFileMiddleware中间件之前注册DefaultFilesMiddleware中间件是因为后者是通过采用URL重写的方式实现的。这个中间件会将针对目录的请求改写成针对默认页面的请求,而最终针对默认页面的请求还需要依赖StaticFileMiddleware中间件来完成。

图7 重命名默认页面

DefaultFilesMiddleware中间件在默认情况下总是以约定的名称在当前请求的目录下定位默认页面。如果作为默认页面的文件没有采用这样的约定命名,比如我们如图7所示的方式将默认页面命名为readme.html,就需要按照如下方式显式指定默认页面的文件名(S1905)。

using Microsoft.Extensions.FileProviders;

var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path);
var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions1 = new DefaultFilesOptions();
var defaultOptions2 = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; defaultOptions1.DefaultFileNames.Add("readme.html");
defaultOptions2.DefaultFileNames.Add("readme.html"); var app = WebApplication.Create();
app
.UseDefaultFiles(defaultOptions1)
.UseDefaultFiles(defaultOptions2)
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions); app.Run();

[1906]设置默认的媒体类型

通过上面演示的实例可以看出,浏览器能够准确地将请求的目标文件的内容正常呈现出来。对HTTP协议具有基本了解的读者应该都知道,响应文件能够在浏览器上被正常显示的基本前提是响应报文通过Content-Type报头携带的媒体类型必须与内容一致。我们的实例演示了针对两种文件类型的请求,一种是JPG文件,另一种是PDF文件,对应的媒体类型分别是“image/jpg”和“application/pdf”,那么用来处理静态文件请求的StaticFileMiddleware中间件是如何解析出对应的媒体类型的呢?

StaticFileMiddleware中间件针对媒体类型的解析是通过一个IContentTypeProvider对象来完成的, FileExtensionContentTypeProvider是对该接口的默认实现。FileExtensionContentTypeProvider根据文件的扩展命名来解析媒体类型。它在内部预定了数百种常用文件扩展名与对应媒体类型之间的映射关系,所以如果发布的静态文件具有标准的扩展名,StaticFileMiddleware中间件就能为对应的响应赋予正确的媒体类型。

图8 重命名默认页面

如果某个文件的扩展名没有在预定义的映射之中,或者需要某个预定义的扩展名匹配不同的媒体类型,那又应该如何解决呢?同样是针对我们演示的这个实例,如果我们以图8所示的方式将“~/wwwroot/img/ dolphin1.jpg”文件的扩展名改成.img,那么StaticFileMiddleware中间件将无法为针对该文件的请求解析出正确的媒体类型。这个问题具有若干不同的解决方案,第一种方案就是按照如下方式让StaticFileMiddleware中间件支持不能识别的文件类型,并为设置一个默认的媒体类型。

var options = new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "image/jpg"
};
var app = WebApplication.Create();
app.UseStaticFiles(options); app.Run();

[1907]映射文件扩展名的媒体类型

上述解决方案只能设置一种默认媒体类型,如果具有多种需要映射成不同媒体类型的文件类型,这种方案就无能为力了,所以最根本的解决方案还是需要将不能识别的文件类型和对应的媒体类型进行映射。由于StaticFileMiddleware中间件使用的IContentTypeProvider对象是可以定制的,所以可以按照如下方式显式地为该中间件指定一个FileExtensionContentTypeProvider对象,然后将缺失的映射添加到这个对象上即可。

using Microsoft.AspNetCore.StaticFiles;

var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.Add(".img", "image/jpg");
var options = new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider
}; var app = WebApplication.Create();
app.UseStaticFiles(options); app.Run();

ASP.NET Core 6框架揭秘实例演示[29]:搭建文件服务器的更多相关文章

  1. ASP.NET Core 6框架揭秘实例演示[07]:文件系统

    ASP.NET Core应用具有很多读取文件的场景,如读取配置文件.静态Web资源文件(如CSS.JavaScript和图片文件等).MVC应用的视图文件,以及直接编译到程序集中的内嵌资源文件.这些文 ...

  2. ASP.NET Core 6框架揭秘实例演示[08]:配置的基本编程模式

    .NET的配置支持多样化的数据源,我们可以采用内存的变量.环境变量.命令行参数.以及各种格式的配置文件作为配置的数据来源.在对配置系统进行系统介绍之前,我们通过几个简单的实例演示一下如何将具有不同来源 ...

  3. ASP.NET Core 6框架揭秘实例演示[09]:配置绑定

    我们倾向于将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置,我们将这个转换过程称为配置绑定.除了将配置树叶子节点配置节的绑定为某种标量对象外,我们还可以直接将一个配置 ...

  4. ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式

    依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...

  5. ASP.NET Core 6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式

    在整个软件开发维护生命周期内,最难的不是如何将软件系统开发出来,而是在系统上线之后及时解决遇到的问题.一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根 ...

  6. ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法

    一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根据当前的运行状态预知未来可能发生的问题,并将问题扼杀在摇篮中.诊断跟踪能够帮助我们有效地纠错和排错&l ...

  7. ASP.NET Core 6框架揭秘实例演示[13]:日志的基本编程模式[上篇]

    <诊断跟踪的几种基本编程方式>介绍了四种常用的诊断日志框架.其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net.NLog和Serilog 等.虽然这些框 ...

  8. ASP.NET Core 6框架揭秘实例演示[14]:日志的进阶用法

    为了对各种日志框架进行整合,微软创建了一个用来提供统一的日志编程模式的日志框架.<日志的基本编程模式>以实例演示的方式介绍了日志的基本编程模式,现在我们来补充几种"进阶" ...

  9. ASP.NET Core 6框架揭秘实例演示[15]:针对控制台的日志输出

    针对控制台的ILogger实现类型为ConsoleLogger,对应的ILoggerProvider实现类型为ConsoleLoggerProvider,这两个类型都定义在 NuGet包"M ...

随机推荐

  1. 论文解读《Bilinear Graph Neural Network with Neighbor Interactions》

    论文信息 论文标题:Bilinear Graph Neural Network with Neighbor Interactions论文作者:Hongmin Zhu, Fuli Feng, Xiang ...

  2. Centos7 安装 MPICH

    查看官网版本 https://www.mpich.org/downloads/ 最新的stable release是mpich 4.0.2,复制下载链接. 安装依赖 mpich需要系列依赖,如果不确定 ...

  3. HBase数据库基础操作

    实验要求: 根据上面给出的学生表Student的信息,执行如下操作: 用Hbase Shell命令创建学生表Student: create 'student','name', 'score' put ...

  4. 动态调试JS脚本文件:(JS源映射 - sourceURL)与 debugger

    我们在进行js调试时经常会对js进行调试,chrome 对js提示对支持非常友好,只需要F12就可以打开chrome的调试器 在sources里面就是页面请求后加载的一些资源文件,我们可以找到我们的j ...

  5. Kafka消息的压缩机制

    最近在做 AWS cost saving 的事情,对于 Kafka 消息集群,计划通过压缩消息来减少消息存储所占空间,从而达到减少 cost 的目的.本文将结合源码从 Kafka 支持的消息压缩类型. ...

  6. Java的标识符与关键字

    目录 Java关键字 总表:java关键字共53个(其中包含两个保留字const,goto) Java标识符 定义 组成 命名规则 视频课程 Java关键字 Java关键字是电脑语言里事先定义的,有特 ...

  7. Cpp的赋值和变量说明

    一命名方式: 1.关键字不能作为变量名 int int;是错误的电脑会提示为非法取名 上面的示例是错误示范,而错误提示告诉了为什么错了记住这错误提示了: 2.的二个知识点: 变量名是分大小写的: in ...

  8. MyBatis - MyBatis的层次结构

    API接口层 规定了一系列接口,能够向外提供接口,对内进行操作. 数据处理层 负责SQL相关处理工作,如:SQL查找.SQL执行.SQL映射等工作. 基础支撑层 提供基础功能支撑,包括连接管理.事务管 ...

  9. 文字轮播与图片轮播?CSS 不在话下

    今天,分享一个实际业务中能够用得上的动画技巧. 巧用逐帧动画,配合补间动画实现一个无限循环的轮播效果,像是这样: 看到上述示意图,有同学不禁会发问,这不是个非常简单的位移动画么? 我们来简单分析分析, ...

  10. 高度灵活可定制的PC布局:头部按钮、左边栏、右边栏、状态栏

    什么是自适应布局 CabloyJS提供了一套布局管理器,实现自适应布局 关于自适应布局的概念,强烈建议先阅读以下两篇文章: 自适应布局:pc = mobile + pad 自适应布局:视图尺寸 什么是 ...