zoukankan      html  css  js  c++  java
  • WebAPI性能优化之压缩解压

    有时候为了提升WebAPI的性能,减少响应时间,我们会使用压缩和解压,而现在大多数客户端浏览器都提供了内置的解压支持。在WebAPI请求的资源越大时,使用压缩对性能提升的效果越明显,而当请求的资源很小时则不需要使用压缩和解压,因为压缩和解压同样也是需要耗费一定的时间的。

    看见老外写了一篇ASP.NET Web API GZip compression ActionFilter with 8 lines of code

    说实话被这标题吸引了,8行代码实现GZip压缩过滤器,我就照着他的去实践了一番,发现居然中文出现乱码。

    按照他的实现方式:

    1、下载DotNetZipLib

    2、解压后添加Ionic.Zlib.dll的dll引用

    3、新建DeflateCompression特性和GZipCompression特性,分别代表Deflate压缩和GZip压缩,这两种压缩方式的实现代码很相似

    不同的地方就是

    actContext.Response.Content.Headers.Add("Content-encoding", "gzip");

    actContext.Response.Content.Headers.Add("Content-encoding", "deflate");

      var compressor = new DeflateStream(
                        output, CompressionMode.Compress,
                        CompressionLevel.BestSpeed)
     var compressor = new GZipStream(
                        output, CompressionMode.Compress,
                        CompressionLevel.BestSpeed)
    复制代码
    using System.Net.Http;
    using System.Web.Http.Filters;
    
    namespace WebAPI.Filter
    {
        public class GZipCompressionAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuted(HttpActionExecutedContext actContext)
            {
                var content = actContext.Response.Content;
                var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
                var zlibbedContent = bytes == null ? new byte[0] :
                CompressionHelper.GZipByte(bytes);
                actContext.Response.Content = new ByteArrayContent(zlibbedContent);
                actContext.Response.Content.Headers.Remove("Content-Type");
                actContext.Response.Content.Headers.Add("Content-encoding", "gzip");
                actContext.Response.Content.Headers.Add("Content-Type", "application/json");
                base.OnActionExecuted(actContext);
            }
        }
    }
    using System.Net.Http;
    using System.Web.Http.Filters;
    
    namespace WebAPI.Filter
    {
        public class DeflateCompressionAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuted(HttpActionExecutedContext actContext)
            {
                var content = actContext.Response.Content;
                var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
                var zlibbedContent = bytes == null ? new byte[0] :
                CompressionHelper.DeflateByte(bytes);
                actContext.Response.Content = new ByteArrayContent(zlibbedContent);
                actContext.Response.Content.Headers.Remove("Content-Type");
                actContext.Response.Content.Headers.Add("Content-encoding", "deflate");
                actContext.Response.Content.Headers.Add("Content-Type", "application/json");
                base.OnActionExecuted(actContext);
            }
        }
    复制代码

    4、添加一个压缩帮助类CompressionHelper

    复制代码
    using System.IO;
    using Ionic.Zlib;
    
    namespace WebAPI.Filter
    {
        public class CompressionHelper
        {
            public static byte[] DeflateByte(byte[] str)
            {
                if (str == null)
                {
                    return null;
                }
    
                using (var output = new MemoryStream())
                {
                    using (
                        var compressor = new DeflateStream(
                        output, CompressionMode.Compress,
                        CompressionLevel.BestSpeed))
                    {
                        compressor.Write(str, 0, str.Length);
                    }
    
                    return output.ToArray();
                }
            }
            public static byte[] GZipByte(byte[] str)
            {
                if (str == null)
                {
                    return null;
                }
                using (var output = new MemoryStream())
                {
                    using (
                        var compressor = new GZipStream(
                        output, CompressionMode.Compress,
                        CompressionLevel.BestSpeed))
                    {
                        compressor.Write(str, 0, str.Length);
                    }
    
                    return output.ToArray();
                }
            }
        }
    }
    复制代码

    5、控制器调用,这里我写的测试代码:

    复制代码
        public class TestController : ApiController
        {
            StringBuilder sb = new StringBuilder();
            
            [GZipCompression]
            public string Get(int id)
            {
                for (int i = 0; i < 1000;i++ )
                {
                    sb.Append("这里是中国的领土" + i);
                }
                return sb.ToString() + DateTime.Now.ToLocalTime() + "," + id;
            }
        }
    复制代码

    先看下不使用压缩,注释//[GZipCompression] 标记,文件大小是26.4kb,请求时间是1.27s

    使用[GZipCompression]标记,添加压缩后,文件大小是2.4kb,响应时间是1.21,Respouse Body明显小了很多,但是响应时间少得并不明显,因为在本地环境下载太快了,而压缩解压却要消耗一定的时间,界面加载的时间主要消耗在onload上了。有个问题:中文显示乱码了。

     使用.net自带的压缩,在System.IO.Compression中提供了对应的类库——GZipStream与DeflateStream。控制器调用代码不变,新建一个CompressContentAttribute.cs类,代码如下:

    复制代码
    using System.Web;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    
    namespace WebAPI.Filter
    {
        // <summary>
        /// 自动识别客户端是否支持压缩,如果支持则返回压缩后的数据
        /// Attribute that can be added to controller methods to force content
        /// to be GZip encoded if the client supports it
        /// </summary>
        public class CompressContentAttribute : ActionFilterAttribute
        {
            /// <summary>
            /// Override to compress the content that is generated by
            /// an action method.
            /// </summary>
            /// <param name="filterContext"></param>
            public override void OnActionExecuting(HttpActionContext filterContext)
            {
                GZipEncodePage();
            }
    
            /// <summary>
            /// Determines if GZip is supported
            /// </summary>
            /// <returns></returns>
            public static bool IsGZipSupported()
            {
                string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
                if (!string.IsNullOrEmpty(AcceptEncoding) &&
                        (AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
                    return true;
                return false;
            }
    
            /// <summary>
            /// Sets up the current page or handler to use GZip through a Response.Filter
            /// IMPORTANT:
            /// You have to call this method before any output is generated!
            /// </summary>
            public static void GZipEncodePage()
            {
                HttpResponse Response = HttpContext.Current.Response;
    
                if (IsGZipSupported())
                {
                    string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
    
                    if (AcceptEncoding.Contains("deflate"))
                    {
                        Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
                                                   System.IO.Compression.CompressionMode.Compress);
                        #region II6不支持此方法,(实际上此值默认为null 也不需要移除)
                        //Response.Headers.Remove("Content-Encoding");
                        #endregion
                        Response.AppendHeader("Content-Encoding", "deflate");
                    }
                    else
                    {
                        Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                                     System.IO.Compression.CompressionMode.Compress);
                        #region II6不支持此方法,(实际上此值默认为null 也不需要移除)
                        //Response.Headers.Remove("Content-Encoding");
                        #endregion
                        Response.AppendHeader("Content-Encoding", "gzip");
                    }
                }
    
                // Allow proxy servers to cache encoded and unencoded versions separately
                Response.AppendHeader("Vary", "Content-Encoding");
            }
        }
    
        /// <summary>
        /// 强制Defalte压缩
        /// Content-encoding:gzip,Content-Type:application/json
        /// DEFLATE是一个无专利的压缩算法,它可以实现无损数据压缩,有众多开源的实现算法。
        /// </summary>
        public class DeflateCompressionAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuting(HttpActionContext filterContext)
            {
                HttpResponse Response = HttpContext.Current.Response;
                Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
                                                  System.IO.Compression.CompressionMode.Compress);
                #region II6不支持此方法,(实际上此值默认为null 也不需要移除)
                //Response.Headers.Remove("Content-Encoding");
                #endregion
                Response.AppendHeader("Content-Encoding", "deflate");
            }
        }
    
        /// <summary>
        /// 强制GZip压缩,application/json
        /// Content-encoding:gzip,Content-Type:application/json
        /// GZIP是使用DEFLATE进行压缩数据的另一个压缩库
        /// </summary>
        public class GZipCompressionAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuting(HttpActionContext filterContext)
            {
                HttpResponse Response = HttpContext.Current.Response;
                Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                                  System.IO.Compression.CompressionMode.Compress);
                #region II6不支持此方法,(实际上此值默认为null 也不需要移除)
                //Response.Headers.Remove("Content-Encoding");
                #endregion
                Response.AppendHeader("Content-Encoding", "gzip");
            }
        }
    }
    复制代码

    运行查看结果,压缩能力比DotNetZipLib略差,但是不再出现乱码了。

    把控制器代码中的标记改为   [DeflateCompression],使用Deflate压缩再来看下效果:

     

    Deflate压缩后,Content-Length值为2538,而GZip压缩Content-Length值为2556,可见Deflate压缩效果更好。

    这里,WebAPI的压缩我都是通过Action过滤器的方式来实现,当然你也可以写在WebAPI中的全局配置中,考虑到有些API接口并不需要使用到压缩,所以就通过Action过滤器的方式来实现了。

    dudu的这篇文章HttpClient与APS.NET Web API:请求内容的压缩与解压在客户端压缩、在服务端解压。

  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/lhxsoft/p/8663637.html
Copyright © 2011-2022 走看看