HTTP协议中的压缩

Http协议中使用Accept-Encoding和Content-Encoding头来表示期望Response内容的编码和当前Request的内容编码。而Http内容的压缩其实是内容编码的子集。所以也通过这两个头来描述Http Request和Response内容的压缩方式。

常用的压缩算法有gzip(采用GNU zip压缩)和deflate(采用zlib的格式压缩),对应的Http头中的值也为:gzip或者deflate.

内容压缩与解压

Web服务器可以配置进行全局压缩和解压。当压缩或者解压需要基于某些逻辑进行判断,则要自行实现。

浏览器(或者部分移动端网络库,包括iOS系统网络库)会对Content-Encoding头的值设为gzip或deflate的Response自动解压。而压缩则需要自己实现。

实现

这里采用DotNetZip库进行压缩和解压。

首先创建两个压缩类来分别对gzip和deflate进行处理:

public abstract class Compressor
{
public abstract byte[] Compress(byte[] originalData); public abstract byte[] Decompress(byte[] compressedData);
} public class DeflateCompressor : Compressor
{
public static string Name => "Deflate"; public override byte[] Compress(byte[] originalData)
{
using (var outputStream = new MemoryStream())
{
using (var gzipStream = new DeflateStream(outputStream, CompressionMode.Compress, CompressionLevel.BestSpeed))
{
gzipStream.Write(originalData, , originalData.Length);
} return outputStream.ToArray();
}
} public override byte[] Decompress(byte[] compressedData)
{
using (var inputStream = new MemoryStream(compressedData))
{
var outputStream = new MemoryStream();
var gzipStream = new DeflateStream(inputStream, CompressionMode.Decompress, CompressionLevel.BestSpeed);
int blockSize = * ;
int readSize = ;
while (readSize > )
{
byte[] buffer = new byte[blockSize];
readSize = gzipStream.Read(buffer, , blockSize);
if (readSize > )
{
outputStream.Write(buffer, , readSize);
}
} outputStream.Flush();
return outputStream.ToArray();
}
}
}
public class GZipCompressor : Compressor
{
public static string Name => "GZip"; public override byte[] Compress(byte[] originalData)
{
using (var outputStream = new MemoryStream())
{
using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress, CompressionLevel.BestSpeed))
{
gzipStream.Write(originalData, , originalData.Length);
} return outputStream.ToArray();
}
} public override byte[] Decompress(byte[] compressedData)
{
using (var inputStream = new MemoryStream(compressedData))
{
var outputStream = new MemoryStream();
var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress, CompressionLevel.BestSpeed);
int blockSize = * ;
int readSize = ;
while (readSize > )
{
byte[] buffer = new byte[blockSize];
readSize = gzipStream.Read(buffer, , blockSize);
if (readSize > )
{
outputStream.Write(buffer, , readSize);
}
} outputStream.Flush();
return outputStream.ToArray();
}
}
}

创建一个DelegatingHandler: CompressHandler

public class CompressHandler : DelegatingHandler
{
private Dictionary<string, Compressor> _supportCompressors = new Dictionary<string, Compressor>(); public void RegisterCompressor(string compressorName, Compressor compressor)
{
if (string.IsNullOrEmpty(compressorName) || compressor == null)
{
throw new InvalidOperationException("parameter is invalid.");
} _supportCompressors[compressorName.ToLower()] = compressor;
} public CompressHandler()
{ } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//拦截请求,对请求进行解压
await DecompressRequestIfNeed(request);
var response = await base.SendAsync(request, cancellationToken);
//对响应进行压缩
await CompressResponseIfNeed(request, response); return response;
} private async Task DecompressRequestIfNeed(HttpRequestMessage request)
{
if (request.Content != null)
{
var originalContentType = request.Content.Headers.ContentType;
foreach (var encoding in request.Content.Headers.ContentEncoding)
{
if (_supportCompressors.ContainsKey(encoding))
{
var compressor = _supportCompressors[encoding];
var compressedBytes = await request.Content.ReadAsByteArrayAsync();
if (compressedBytes == null || compressedBytes.Length == )
{
// don't need to decompress, since the content is empty.
break;
} //基于Content-Encoding进行解压
var decompressedBytes = compressor.Decompress(compressedBytes);
var byteContent = new ByteArrayContent(decompressedBytes);
request.Content = byteContent;
//恢复原始的ContentTypeHeader到新的RequestContent中
request.Content.Headers.ContentType = originalContentType; break;
}
}
}
} private async Task CompressResponseIfNeed(HttpRequestMessage request, HttpResponseMessage response)
{
if (response.Content != null)
{
foreach (var acceptEncoding in request.Headers.AcceptEncoding)
{
if (_supportCompressors.ContainsKey(acceptEncoding.Value.ToLower()))
{
var originalBytes = await response.Content.ReadAsByteArrayAsync();
if (originalBytes == null || originalBytes.Length == )
{
// don't need to compress, since the content is empty.
break;
}
//基于客户端能接受的压缩算法进行压缩
var compressor = _supportCompressors[acceptEncoding.Value];
var compresedBytes = compressor.Compress(originalBytes);
//重新设置新的Response Content和Header
response.Content = new ByteArrayContent(compresedBytes);
response.Content.Headers.ContentEncoding.Add(acceptEncoding.Value);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); break;
}
}
}
}
}

将CompressHandler添加到WebApi的MessageHandler列表中来拦截Http请求和响应, 来执行压缩和解压。

public static void Register(HttpConfiguration config)
{ var compressHandler = new CompressHandler();
//将GZip和Deflate压缩器注册到CompressHandler
compressHandler.RegisterCompressor(GZipCompressor.Name, new GZipCompressor());
compressHandler.RegisterCompressor(DeflateCompressor.Name, new DeflateCompressor());
config.MessageHandlers.Add(compressHandler);
config.MapHttpAttributeRoutes();
}

自定义DelegatingHandler为ASP.NET Web Api添加压缩与解压的功能的更多相关文章

  1. 为Asp.Net Web Api添加Http基本认证

    Asp.net Web Api提供了RESTFul web服务的编程接口.默认RESTFul 服务没有提供任何验证或者基于角色的验证,这显然不适合Put.Post.Delete这些操作.Aps.net ...

  2. 为IIS Host ASP.NET Web Api添加Owin Middleware

    将OWIN App部署在IIS上 要想将Owin App部署在IIS上,只添加Package:Microsoft.OWIN.Host.SystemWeb包即可.它提供了所有Owin配置,Middlew ...

  3. ASP.NET Web API 入门示例详解

    REST服务已经成为最新的服务端开发趋势,ASP.NET Web API即为.NET平台的一种轻量级REST架构. ASP.NET Web API直接借鉴了ASP.NET MVC的设计,两者具有非常类 ...

  4. Asp.net中文件的压缩与解压

    这里笔者为大家介绍在asp.net中使用文件的压缩与解压.在asp.net中使用压缩给大家带来的好处是显而易见的,首先是减小了服务器端文件存储的空间,其次下载时候下载的是压缩文件想必也会有效果吧,特别 ...

  5. asp.net web api添加统一异常处理

    1.自定义异常处理过滤器 /// <summary> /// 自定义异常处理过滤器 /// </summary> public class CustomExceptionFil ...

  6. JAVA自带API的压缩与解压

    Java API中的 java.util.zip.*;包下包含了Java对于压缩文件的所有相关操作.我们可以使用该包中的方法,结合IO中的相关知识,进行文件的压缩和解压缩相关操作. ZipFile j ...

  7. Asp.net Web Api添加异常筛选器

    一.定义一个异常筛选器 using System;using System.Collections.Generic;using System.Linq;using System.Web;using S ...

  8. ASP.NET Web API中使用GZIP 或 Deflate压缩

    对于减少响应包的大小和响应速度,压缩是一种简单而有效的方式. 那么如何实现对ASP.NET Web API 进行压缩呢,我将使用非常流行的库用于压缩/解压缩称为DotNetZip库.这个库可以使用Nu ...

  9. 用Middleware给ASP.NET Core Web API添加自己的授权验证

    Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做到完全的前后端分离.API的实现方式有很 多,可以用ASP.NET Core.也可以用ASP.NET Web ...

随机推荐

  1. web前端 —— 移动端知识的一些总结

    个人在移动端的一些总结归纳,有新的知识点会一直更新 一.css部分 1.meta标签 <meta name="viewport" content="width=de ...

  2. Symmetrical Network Acceleration with EBS 12

    Andy Tremayne, my esteemed colleague and fellow blogger, has published a new whitepaper that discuss ...

  3. taskset -pc PID 查看线程占用cpu核

    taskset -pc  PID 可以用于 查看 当前线程 对应绑定的 在 哪个核上面. 这个 可以用于 程序优化, 查看 哪个线程占用的 cpu 比重比较高 首先 可以通过  top  -H   - ...

  4. 013——数组(十三) array_push array_rand array_reverse

    <?php /* 数组 array_push array_rand array_reverse */ //array_push()在数组的末端,增加一个或多个元素,入栈 /*$array = a ...

  5. NEU 1496 Planar map 计算几何,点到线段距离 难度:0

    问题 H: Planar map 时间限制: 1 Sec  内存限制: 128 MB提交: 24  解决: 22[提交][状态][讨论版] 题目描述 Tigher has work for a lon ...

  6. 【zzuli-2259】matrix

    题目描述 在麦克雷的面前有N个数,以及一个R*C的矩阵.现在他的任务是从N个数中取出 R*C 个,并填入这个矩阵中.矩阵每一行的法值为本行最大值与最小值的差,而整个矩阵的法值为每一行的法值的最大值.现 ...

  7. Ti IPNC Web网页之ActiveX控件

    Ti IPNC Web网页之ActiveX控件 本篇介绍关于TI IPNC网页中播放器相关的东西. gStudio工程中添加播放器并控制播放器 打开IPNC网页时首先会自动下载ActiveX控件并安装 ...

  8. Django中关于事务的代码编写

    Django中对于数据库的事务,默认每执行一句数据库操作,便会自动提交. 在Django中可以通过django.db.transaction模块提供的atomic来定义一个事务,atomic提供两种用 ...

  9. 关于block和inline元素的float

    CSS float 浮动属性 本篇主要介绍float属性:定义元素朝哪个方向浮动. 目录 1. 页面布局方式:介绍文档流.浮动层以及float属性. 2. float:left :介绍float为 l ...

  10. Java对多线程的支持

    Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行. 在Java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程 ...