zoukankan      html  css  js  c++  java
  • 《DotNet Web应用单文件部署系列》二、打包wwwroot文件夹

           在这篇文章中,你将学到web缓存规则,文件传输中用到的压缩格式,以及如何手写代码响应请求。最后还能学到快速打包wwwroot文件夹组件用法。

    一、了解Response Header

      当第一次加载程序时,浏览器将打开页面并下载所有的资源连接。假如页面没有错误返回都是正确那么就是返回文件数据和Http Status为200 -OK的状态

      我们看下这个jquery.min.js文件Http请求对应的Response Header,这里会包含ETag值。HTTP内容如下:

        ETag: 1d7a4ae31f17d74

      ETag :HTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖。如果给定URL中的资源更改,则一定要生成新的Etag值。 因此Etags类似于指纹,也可能被某些服务器用于跟踪。 比较etags能快速确定此资源是否变化,但也可能被跟踪服务器永久存留。

      

      如果再次请求这个地址的话,浏览器将发送ETag到服务端,如果两个值没有变化,那么服务端会发送304状态到浏览器,那么浏览器将使用之前的资源而不是重新下载一份。

        If-None-Match: 1d7a4ae31f17d74

     

      将文件都缓存到了客户端,这样就提高了浏览器的响应性能,并且通过304状态,浏览器与服务端的请求流量得以减少。

          

           加快浏览器的响应性能,还可以设置两种方案,

      1)减少浏览器与服务端的请求次数;可以在响应头内设置Expires,Cache-Control两个参数。

        Expires:响应头包含日期/时间, 即在此时候之后,响应过期。

             Cache-Control:通用消息头字段,被用于在http请求和响应中,通过指定指令来实现缓存机制。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

      2)压缩文件,减少返回流量,从而提高响应性能。

        在HTTP 请求时,客户端会发送Accept-Encoding请求头给服务端,服务端就可以根据Accept-Encoding请求头匹配到压缩方式,将文件压缩后返回。

    二、DotNet 5 手写代码响应请求

           先放主代码

    1         [HttpGet("_/js/jquery.min.js")]
    2         public IActionResult __js_jquery_min_js()
    3         {
    4             if (SetResponseHeaders("4F252523D4AF0B478C810C2547A63E19") == false) { return StatusCode(304); }
    5             const string s = "base64编码";
    6             var bytes = UseCompressBytes(s);
    7             return File(bytes, "text/javascript");
    8         }

      第一行,我们定义请求方式及请求地址。

      第二行,我们将请求地址转成可用的方法名。

      第四行,我们设置返回头,如果返回头有相同的ETAG值,就返回304。ETAG值可使用Hash值,如Md5。

      第五行,获取文件的base64编码,注意文件先经Brotli 算法压缩,然后转成base64编码的。这样有效地减少文件大小。

      第六行,我们尝试以压缩的编码返回。

      第七行,我们返回文件内容及Content-Type类型。

           SetResponseHeaders方法如下

     1     SetResponseHeaders方法如下
     2         private bool SetResponseHeaders(string etag)
     3         {
     4             if (Request.Headers["If-None-Match"] == etag) { return false; }
     5             Response.Headers["Cache-Control"] = "max-age=315360000";
     6             Response.Headers["Etag"] = etag;
     7             Response.Headers["Date"] = DateTime.Now.ToString("r");
     8             Response.Headers["Expires"] = DateTime.Now.AddYears(100).ToString("r");
     9             return true;
    10         }

       我们先对比etag值是否相同,如果相同返回false。不同就设置etag值以及过期时间100年有效。

          

           UseCompressBytes方法如下:

     1         private byte[] UseCompressBytes(string s)
     2         {
     3             var bytes = Convert.FromBase64String(s);
     4             var sp = Request.Headers["Accept-Encoding"].ToString().Replace(" ", "").ToLower().Split(',');
     5             if (sp.Contains("br")) {
     6                 Response.Headers["Content-Encoding"] = "br";
     7             } else  {
     8                 using (MemoryStream stream = new MemoryStream(bytes)) {
     9                     using (BrotliStream zStream = new BrotliStream(stream, CompressionMode.Decompress)) {
    10                         using (var resultStream = new MemoryStream()) {
    11                             zStream.CopyTo(resultStream);
    12                             bytes = resultStream.ToArray();
    13                         }
    14                     }
    15                 }
    16                 if (sp.Contains("gzip")) {
    17                     Response.Headers["Content-Encoding"] = "gzip";
    18                     using (MemoryStream stream = new MemoryStream()) {
    19                         using (GZipStream zStream = new GZipStream(stream, CompressionMode.Compress)) {
    20                             zStream.Write(bytes, 0, bytes.Length);
    21                             zStream.Close();
    22                         }
    23                         bytes = stream.ToArray();
    24                     }
    25                 }
    26             }
    27             return bytes;
    28         }

           第一步,判断是否支持Brotli 算法压缩,如果支持就马上返回。

           第二步,使用Brotli 算法解压。

           第三步,判断是否支持gzip算法压缩,如果支持使用gzip算法压缩,然后返回。

           第四步,返回原bytes。

    三、快速压缩打包wwwroot文件夹

           打包wwwroot文件夹很简单,但文件一个一个手写就会觉得很麻烦。这时就需要ToolGood.WwwRoot组件,在Nuget上直接获取。

          

    3.1、快速上手

           1)新建一个控制台应用程序,

      2)从Nuget上引用ToolGood.WwwRoot组件,

           3)添加以下代码

    1                 WwwRootSetting setting = new WwwRootSetting();
    2                 setting.NameSpace = "ToolGood.TextFilter.Controllers";
    3                 setting.InFolderPath = @"你的项目路径wwwroot";
    4                 setting.OutFolderPath = @"你的项目路径Controllerswwwroot";
    5 
    6                 setting.ExcludeFileSuffixs.Add(".old.js");
    7                 setting.ExcludeFileSuffixs.Add(".map");
    8                 setting.BuildControllers();

           4)运行控制台应用程序,就会生成相应的cs文件。

     

    2.2、WwwRootSetting参数简介

           NameSpace参数设置命名空间

           InFolderPath参数指定wwwroot目录

           OutFolderPath参数指定输出目录

           ExcludeFileSuffixs参数依据文件后缀排除文件

           ExcludeFiles参数 依据文件排除文件

    2.3、其他相关

        1)每个生成的文件都有 #if RELEASE 和 #endif,保证调试模式下不会被编译。

        2)app.UseStaticFiles();前面添加 #if DEBUG 后面添加 #endif ,保证生成后不会使用本地静态文件。

        3)程序更新后,静态文件过期问题。网上有很多成熟的方案,这里介绍一个最简单的方法,使用静态文件+程序版本号来解决,如:

          <script src="_/js/ok.js?v=20210911"></script>

    后记:

           .net 5 单文件运行时会将dll文件释放到内存内,技术高超的人还是能从内存截取dll文件。提高dll文件反编译成本,我们可以将dll文件混淆。

           dll文件混淆后会带来一系列问题,主要是操作麻烦,所以下几篇将介绍dll文件混淆、VS调时使用项目源文件,只在生成时使用dll混淆文件。

  • 相关阅读:
    struts2的结果类型
    struts2的基本配置
    struts2拦截器
    struts2入门程序
    struts2工作原理
    Mysql处理字符串函数(转)
    oracle调用java方法的例子(下面所有代码都是在sql/plus中写)
    Oracle数据库中调用Java类开发存储过程、函数的方法
    Oracle触发器反向调用Java程序
    javaweb项目打包成war包
  • 原文地址:https://www.cnblogs.com/toolgood/p/15253909.html
Copyright © 2011-2022 走看看