[小技巧]ASP.NET Core中如何预压缩静态文件

原文:[小技巧]ASP.NET Core中如何预压缩静态文件



原文地址:Pre-compressed static files with ASP.NET Core
作者:Gunnar Peipman
译者:Lamond Lu
译文:https://www.cnblogs.com/lwqlun/p/10552131.html
示例代码:https://github.com/lamondlu/CompressedStaticFileSample



Web应用程序的优化是非常重要,因为使用更少的CPU,占用更少的带宽可以减少项目的费用。 在ASP.NET Core中我们可以很容易的启用响应压缩,但是针对预压缩文件,就需要做一些额外的功能了。 这篇博客文章展示了如何在ASP.NET Core中预压缩静态文件。

为什么需要预压缩文件?#


虽然在从服务器请求文件时, 我们可以动态压缩文件,但这意味这Web服务器需要做更多的额外工作。 其实只有在新的应用程序部署时才会更改要压缩的文件。 越好的压缩效果需要CPU做的工作就越多。


这个事实让我们产生一个疑问:是否有可能在不对其进行反复压缩的情况下提供这些文件? 幸运的是,这个问题答案是肯定的 - 是的,我们可以在ASP.NET Core中通过扩展静态文件中间件来做到这一点。

创建预压缩文件#


为了让整个演示尽量简单,我们可以使用7-Zip来压缩磁盘上的静态文件。 以下是压缩默认ASP.NET Core MVC应用程序的site.css文件时7-Zip的对话框窗口。



这里你可能注意到我启用了Ultra压缩。这显然不是我们希望在Web服务器上动态压缩的方法,因为它太消耗CPU了。



正常情况下,这里可以使用Gulp来完成文件捆绑和收缩的功能,本文中暂时不会介绍这个。


提供压缩文件#


这里我参考了Stack Overflow上的一个简单解决方案(How to gzip static content in ASP.NET Core in a self host environment. )。它处理了Javascript和CSS文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<DIV class=esa-clipboard-button data-clipboard-target="#copy_target_0" data-tips="复制代码">Copy</DIV><wyn>app<SPAN class=hljs-variable>.UseStaticFiles</SPAN>(<SPAN class=hljs-keyword>new</SPAN> StaticFileOptions
{
    OnPrepareResponse = <SPAN class=hljs-keyword>context</SPAN> =>
    {
        IHeaderDictionary headers = <SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.Context</SPAN><SPAN class=hljs-variable>.Response</SPAN><SPAN class=hljs-variable>.Headers</SPAN>;
        <SPAN class=hljs-keyword>string</SPAN> contentType = headers[<SPAN class=hljs-string>"Content-Type"</SPAN>];
        <SPAN class=hljs-keyword>if</SPAN> (contentType == <SPAN class=hljs-string>"application/x-gzip"</SPAN>)
        {
            <SPAN class=hljs-keyword>if</SPAN> (<SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.File</SPAN><SPAN class=hljs-variable>.Name</SPAN><SPAN class=hljs-variable>.EndsWith</SPAN>(<SPAN class=hljs-string>"js.gz"</SPAN>))
            {
                contentType = <SPAN class=hljs-string>"application/javascript"</SPAN>;
            }
            <SPAN class=hljs-keyword>else</SPAN> <SPAN class=hljs-keyword>if</SPAN> (<SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.File</SPAN><SPAN class=hljs-variable>.Name</SPAN><SPAN class=hljs-variable>.EndsWith</SPAN>(<SPAN class=hljs-string>"css.gz"</SPAN>))
            {
                contentType = <SPAN class=hljs-string>"text/css"</SPAN>;
            }
            headers<SPAN class=hljs-variable>.Add</SPAN>(<SPAN class=hljs-string>"Content-Encoding"</SPAN>, <SPAN class=hljs-string>"gzip"</SPAN>);
            headers[<SPAN class=hljs-string>"Content-Type"</SPAN>] = contentType;
        }
    }
});

当然Javascript和CSS文件并不是唯一需要压缩的文件类型。所以这里我们不能把contentType写死。这里我采用了.NET Core Tutorials站点中提供的一个解决方案( Getting A Mime Type From A File Name In .NET Core)。对我来说这个方案已经足够简单。

1
2
3
4
5
6
<DIV class=esa-clipboard-button data-clipboard-target="#copy_target_1" data-tips="复制代码">Copy</DIV><wyn><SPAN class=hljs-keyword>var</SPAN> provider = <SPAN class=hljs-keyword>new</SPAN> FileExtensionContentTypeProvider();
<SPAN class=hljs-keyword>string</SPAN> contentType;
<SPAN class=hljs-keyword>if</SPAN> (!provider.TryGetContentType(fileName, <SPAN class=hljs-keyword>out</SPAN> contentType))
{
    contentType = <SPAN class=hljs-string>"application/octet-stream"</SPAN>;
}

这里我把2个方案合并在里一起,产生了最终解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<DIV class=esa-clipboard-button data-clipboard-target="#copy_target_2" data-tips="复制代码">Copy</DIV><wyn><SPAN class=hljs-keyword>var</SPAN> mimeTypeProvider = <SPAN class=hljs-keyword>new</SPAN> FileExtensionContentTypeProvider();
 
app<SPAN class=hljs-variable>.UseStaticFiles</SPAN>(<SPAN class=hljs-keyword>new</SPAN> StaticFileOptions
{
    OnPrepareResponse = <SPAN class=hljs-keyword>context</SPAN> =>
    {
        <SPAN class=hljs-keyword>var</SPAN> headers = <SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.Context</SPAN><SPAN class=hljs-variable>.Response</SPAN><SPAN class=hljs-variable>.Headers</SPAN>;
        <SPAN class=hljs-keyword>var</SPAN> contentType = headers[<SPAN class=hljs-string>"Content-Type"</SPAN>];
 
        <SPAN class=hljs-keyword>if</SPAN> (contentType != <SPAN class=hljs-string>"application/x-gzip"</SPAN> && !<SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.File</SPAN><SPAN class=hljs-variable>.Name</SPAN><SPAN class=hljs-variable>.EndsWith</SPAN>(<SPAN class=hljs-string>".gz"</SPAN>))
        {
            <SPAN class=hljs-keyword>return</SPAN>;
        }
 
        <SPAN class=hljs-keyword>var</SPAN> fileNameToTry = <SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.File</SPAN><SPAN class=hljs-variable>.Name</SPAN><SPAN class=hljs-variable>.Substring</SPAN>(<SPAN class=hljs-number>0</SPAN>, <SPAN class=hljs-keyword>context</SPAN><SPAN class=hljs-variable>.File</SPAN><SPAN class=hljs-variable>.Name</SPAN><SPAN class=hljs-variable>.Length</SPAN> - <SPAN class=hljs-number>3</SPAN>);
 
        <SPAN class=hljs-keyword>if</SPAN> (mimeTypeProvider<SPAN class=hljs-variable>.TryGetContentType</SPAN>(fileNameToTry, out <SPAN class=hljs-keyword>var</SPAN> mimeType))
        {
            headers<SPAN class=hljs-variable>.Add</SPAN>(<SPAN class=hljs-string>"Content-Encoding"</SPAN>, <SPAN class=hljs-string>"gzip"</SPAN>);
            headers[<SPAN class=hljs-string>"Content-Type"</SPAN>] = mimeType;
        }
    }
});

至此,使用以上的代码,本文的主题就被解决了。


针对那些想直接使用现成库的开发人员,可以使用Nuget直接下载Peter Andersson做好的中间件。



Install-Package CompressedStaticFiles -Version 1.0.4

总结#


虽然使用预压缩文件不是Web开发的主流,但它仍然可以节省CPU和带宽。 压缩静态文件可以作为ASP.NET Core应用程序构建的一个步骤。 尽管ASP.NET Core开箱即不支持预压缩文件,但我们依然可以通过扩展静态文件中间件,使其支持预压缩文件。