天天看點

《DotNet Web應用單檔案部署系列》二、打包wwwroot檔案夾

       在這篇文章中,你将學到web緩存規則,檔案傳輸中用到的壓縮格式,以及如何手寫代碼響應請求。最後還能學到快速打包wwwroot檔案夾元件用法。

一、了解Response Header

  當第一次加載程式時,浏覽器将打開頁面并下載下傳所有的資源連接配接。假如頁面沒有錯誤傳回都是正确那麼就是傳回檔案資料和Http Status為200 -OK的狀态

《DotNet Web應用單檔案部署系列》二、打包wwwroot檔案夾

  我們看下這個jquery.min.js檔案Http請求對應的Response Header,這裡會包含ETag值。HTTP内容如下:

    ETag: 1d7a4ae31f17d74

  ETag :HTTP響應頭是資源的特定版本的辨別符。這可以讓緩存更高效,并節省帶寬,因為如果内容沒有改變,Web伺服器不需要發送完整的響應。而如果内容發生了變化,使用ETag有助于防止資源的同時更新互相覆寫。如果給定URL中的資源更改,則一定要生成新的Etag值。 是以Etags類似于指紋,也可能被某些伺服器用于跟蹤。 比較etags能快速确定此資源是否變化,但也可能被跟蹤伺服器永久存留。

  

  如果再次請求這個位址的話,浏覽器将發送ETag到服務端,如果兩個值沒有變化,那麼服務端會發送304狀态到浏覽器,那麼浏覽器将使用之前的資源而不是重新下載下傳一份。

    If-None-Match: 1d7a4ae31f17d74

《DotNet Web應用單檔案部署系列》二、打包wwwroot檔案夾

  将檔案都緩存到了用戶端,這樣就提高了浏覽器的響應性能,并且通過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 = @"你的項目路徑\Controllers\wwwroot";
5 
6                 setting.ExcludeFileSuffixs.Add(".old.js");
7                 setting.ExcludeFileSuffixs.Add(".map");
8                 setting.BuildControllers();      

       4)運作控制台應用程式,就會生成相應的cs檔案。

《DotNet Web應用單檔案部署系列》二、打包wwwroot檔案夾

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混淆檔案。

《DotNet Web應用單檔案部署系列》二、打包wwwroot檔案夾