【.NET Core框架】静态文件服务器

简介

ASP.NET Core提供以下3个中间件来处理针对静态文件的请求,它们均定义在NuGet包“Microsoft.AspNetCore.StaticFiles”中,利用这3个中间件完全可以搭建一个基于Web的文件服务器

  • StaticFileMiddleware:处理静态文件的请求
  • DirectoryBrowserMiddleware:浏览器呈现目标目录的结构
  • DefaultFilesMiddleware:默认页面的呈现

提供静态文件

静态文件默认存放在Web根目录(Web Root)中,路径为项目根目录(Content Root)下的wwwroot文件夹,也就是{Content Root}/wwwroot
如果你调用了Host.CreateDefaultBuilder方法,那么在该方法中,会通过UseContentRoot方法,将程序当前工作目录Directory.GetCurrentDirectory()设置为项目根目录。
MVC中默认开启静态文件中间件(在Configure方法中 app.UseStaticFiles()),即wwwroot目录下的文件均可以访问。
如果有需要,你也可以通过UseWebRoot扩展方法将默认的路径{Content Root}/wwwroot修改为自定义目录

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            // 配置静态资源的根目录为 mywwwroot, 默认为 wwwroot
            webBuilder.UseWebRoot("mywwwroot");
            webBuilder.UseStartup<Startup>();
        });

注意,确保 wwwroot 下的文件的属性为“如果较新则复制”或“始终复制”。

我们通过UseStaticFiles扩展方法,来注册静态文件中间件StaticFileMiddleware

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStaticFiles();
}

如果你的项目中启用SwaggerUI,那么你会发现,即使你没有手动通过调用UseStaticFiles()添加中间件,你也可以访问 wwwroot 文件下的文件,这是因为 SwaggerUIMiddleware 中使用了 StaticFileMiddleware

提供Web根目录之外的文件

上面我们已经能够提供 wwwroot 文件夹内的静态文件了,那如果我们的文件不在 wwwroot 文件夹内,那如何提供呢?

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 提供 wwwroot 静态文件
    app.UseStaticFiles();

    // 提供 files 静态文件
    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")),
        // 指定文件的访问路径,允许与 FileProvider 中的文件夹不同名
        // 如果不指定,则可通过 http://localhost:5000/file.json 获取,
        // 如果指定,则需要通过 http://localhost:5000/files/file.json 获取
        RequestPath = "/files",
        OnPrepareResponse = ctx =>
        {
            // 配置前端缓存 600s(为了后续示例的良好运行,建议先不要配置该Header)
            ctx.Context.Response.Headers.Add(HeaderNames.CacheControl, "public,max-age=600");
        }
    });
}

StaticFileOptions参数类

定义在StaticFileOptions中的前三个属性都与媒体类型的解析有关,其中ContentTypeProvider属性返回一个根据请求相对地址解析出媒体类型的IContentTypeProvider对象。如果这个IContentTypeProvider对象无法正确解析出目标文件的媒体类型,就可以利用DefaultContentType设置一个默认媒体类型。但只有将另一个名为ServeUnknownFileTypes的属性设置为True,中间件才会采用这个默认设置的媒体类型。
HttpsCompression:采用HTTPS方法请求的文件是否应该被压缩,该属性的默认值为Compress(即默认情况下会对文件进行压缩)
OnPrepareResponse:输出之前执行,利用这个委托对象可以对最终的响应进行定制

public abstract class SharedOptionsBase
{
    // 用于自定义静态文件的相对请求路径
    public PathString RequestPath { get; set; }

    // 文件提供程序
    public IFileProvider FileProvider { get; set; }

    // 是否补全路径末尾斜杠“/”,并重定向
    public bool RedirectToAppendTrailingSlash { get; set; }
}

public class StaticFileOptions : SharedOptionsBase
{
    // ContentType提供程序
    public IContentTypeProvider ContentTypeProvider { get; set; }
    
    // 如果 ContentTypeProvider 无法识别文件类型,是否仍作为默认文件类型提供
    public bool ServeUnknownFileTypes { get; set; }
    
    // 当 ServeUnknownFileTypes = true 时,若出现无法识别的文件类型,则将该属性的值作为此文件的类型
    // 当 ServeUnknownFileTypes = true 时,必须赋值该属性,才会生效
    public string DefaultContentType { get; set; }
    
    // 当注册了HTTP响应压缩中间件时,是否对文件进行压缩
    public HttpsCompressionMode HttpsCompression { get; set; } = HttpsCompressionMode.Compress;
    
    // 在HTTP响应的 Status Code 和 Headers 设置完毕之后,Body 写入之前进行调用
    // 用于添加或更改 Headers
    public Action<StaticFileResponseContext> OnPrepareResponse { get; set; }
}

案例演示:

var staticfile = new StaticFileOptions();
//设置当文件的后缀名不在已知的范围内的时候返回404
staticfile.ServeUnknownFileTypes = true;
//对于未找到对应mime的使用下面设置的默认mime值(要求ServeUnknownFileTypes=true)
staticfile.DefaultContentType = "application/x-msdownload";
// 手动设置对应的 MIME TYPE
var provider = new FileExtensionContentTypeProvider();
provider.Mappings.Add(".log", "text/plain");
provider.Mappings.Add(".dwg", "application/x-dwg");
staticfile.ContentTypeProvider = provider;
app.UseStaticFiles(staticfile);

媒体类型

StaticFileMiddleware中间件可以根据目标文件后缀输出正确的媒体类型,对于客户端来说,如果无法确定媒体类型,获取的文件就像是一部无法解码的天书,毫无价值。StaticFileMiddleware中间件利用指定的IContentTypeProvider对象来解析媒体类型。如下面的代码片段所示,IContentTypeProvider接口定义了唯一的方法TryGetContentType,从而根据当前请求的相对路径来解析这个作为输出参数的媒体类型。

public interface IContentTypeProvider
{
    bool TryGetContentType(string subpath, out string contentType);
}

StaticFileMiddleware中间件默认使用FileExtensionContentTypeProvider类型。顾名思义FileExtensionContentTypeProvider利用物理文件的扩展名来解析对应的媒体类型,并利用其Mappings属性表示的字典维护了扩展名与媒体类型之间的映射关系。常用的数百种标准的文件扩展名和对应的媒体类型之间的映射关系都会保存在这个字典中。如果发布的文件具有一些特殊的扩展名,或者需要将现有的某些扩展名映射为不同的媒体类型,都可以通过添加或者修改扩展名/媒体类型之间的映射关系来实现。

public class FileExtensionContentTypeProvider : IContentTypeProvider
{
    public IDictionary<string, string> Mappings { get; }

    public FileExtensionContentTypeProvider();
    public FileExtensionContentTypeProvider(IDictionary<string, string> mapping);

    public bool TryGetContentType(string subpath, out string contentType);
}

启用目录浏览

通过UseDirectoryBrowser,注册DirectoryBrowserMiddleware中间件,可以让我们在浏览器中以目录的形式来访问文件列表。

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDirectoryBrowser();
      }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
          // 通过 http://localhost:5000,即可访问 wwwroot 目录
          app.UseDirectoryBrowser();

          // 通过 http://localhost:5000/files,即可访问 files 目录
          app.UseDirectoryBrowser(new DirectoryBrowserOptions
          {
              // 如果指定了没有在 UseStaticFiles 中提供的文件目录,虽然可以浏览文件列表,但是无法访问文件内容
              FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")),
              // 这里一定要和 StaticFileOptions 中的 RequestPath 一致,否则会无法访问文件
              RequestPath = "/files"
          });
        }

相对路径设置为"/img",那么则可以通过 http://localhost:5000/img/ 进行目录的浏览,并打开文件

启用默认文件

要提供默认文件,必须在UseStaticFiles前调用 UseDefaultFilesUseDefaultFiles实际上用于重写URL,不提供文件,需通过UseStaticFiles启用静态文件中间件来提供文件。
默认文件的访问顺序为:default.htm→default.html→index.htm→index.html,且必须放在wwwroot目录下

            // 会去 wwwroot 寻找 default.htm 、default.html 、index.htm 或 index.html 文件作为默认页
            //app.UseDefaultFiles();

            // 设置 files 目录的默认页
            var defaultFilesOptions = new DefaultFilesOptions();
            defaultFilesOptions.DefaultFileNames.Clear();
            // 指定默认页名称
            defaultFilesOptions.DefaultFileNames.Add("index1.html");
            // 指定请求路径
            defaultFilesOptions.RequestPath = "/files";
            // 指定默认页所在的目录
            defaultFilesOptions.FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files"));
            app.UseDefaultFiles(defaultFilesOptions);

            //作用于wwwroot目录,即根目录
            app.UseStaticFiles();

UseFileServer(融合静态文件、默认文件、目录浏览)

            //提供静态文件和默认文件,未启用目录浏览。
            app.UseFileServer();

            //提供静态文件、启用目录浏览。
            //app.UseFileServer(enableDirectoryBrowsing: true);

            //启用静态文件、默认文件和及 MyStaticFiles 的目录浏览
            //app.UseStaticFiles();
            //app.UseFileServer(new FileServerOptions
            //{
            //    FileProvider = new PhysicalFileProvider(
            //        Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
            //        RequestPath = "/MyStaticFiles",
            //        EnableDirectoryBrowsing = true,//启用目录浏览
            //        EnableDefaultFiles=true,//启用默认文件
            //});

参考:
https://www.cnblogs.com/artech/p/static-file-middleware-01.html
https://www.cnblogs.com/xiaoxiaotank/p/15496538.html

posted @ 2020-01-05 14:47  .Neterr  阅读(663)  评论(0编辑  收藏  举报