Azure 应用服务 - 请求中的 GZip 压缩

Posted

技术标签:

【中文标题】Azure 应用服务 - 请求中的 GZip 压缩【英文标题】:Azure App Service- GZip Compression in Request 【发布时间】:2018-07-30 23:48:54 【问题描述】:

使用此代码(没有请求压缩部分),我可以从 Azure 应用服务(具有离线同步功能的 Xamarin.Froms 应用)获取 gzip 压缩内容。但是当我尝试 gzip 请求 http-content 时,我得到一个“错误请求”。

有什么想法吗?是否可以使用 Azure 应用服务对请求内容进行 gzip 压缩?

namespace XXX.XXX.XXX.XXX.XXX

    public class HttpGZipClientHandler : System.Net.Http.HttpClientHandler
    
        long time = 0;
        private long _downloadedBytesFromServer;
        private long _downloadedProcessedBytes;
        private long _intendedUploadedBytesToServer;
        private long _uploadedBytesToServer;
        private long _additionalTimeOverhead = 0;

        public override bool SupportsAutomaticDecompression  get  return true;  
        public long DownloadedBytesFromServer  get  return _downloadedBytesFromServer;  
        public long DownloadedProcessedBytes  get  return _downloadedProcessedBytes;  
        public long IntendedUploadedBytesToServer  get  return _intendedUploadedBytesToServer;  
        public long UploadedBytesToServer  get  return _uploadedBytesToServer;  
        public long AdditionalTimeOverhead  get  return _additionalTimeOverhead;  

        public void ResetStatistics()
        
            _downloadedBytesFromServer = 0;
            _downloadedProcessedBytes = 0;
            _intendedUploadedBytesToServer = 0;
            _uploadedBytesToServer = 0;
            _additionalTimeOverhead = 0;
        

        protected override async Task<System.Net.Http.HttpResponseMessage> SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        

            //Save content headers before compressing
            System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>> savedContentHeaders = new Dictionary<string, IEnumerable<string>>();
            foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in request.Content.Headers)
            
                savedContentHeaders.Add(keyValue.Key, keyValue.Value);
            

            //Compress request content
            System.Diagnostics.Stopwatch sp1 = new System.Diagnostics.Stopwatch();
            sp1.Start();

            _intendedUploadedBytesToServer += request.Content.Headers.ContentLength.HasValue ? request.Content.Headers.ContentLength.Value : 0;

            await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
            request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Compress);

            byte[] uploadedBytes = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
            _uploadedBytesToServer += uploadedBytes.Length;

            sp1.Stop();
            _additionalTimeOverhead += sp1.ElapsedMilliseconds;

            //Set headers
            foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in savedContentHeaders)
            
                request.Content.Headers.Add(keyValue.Key, keyValue.Value);
            

            request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
            request.Content.Headers.Add("Content-Encoding", "gzip");

            //Execute request
            System.Net.Http.HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
            _downloadedBytesFromServer += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;

            //Decompress response content
            if (response.Content.Headers.ContentEncoding.Contains("gzip"))
            
                System.Diagnostics.Stopwatch sp2 = new System.Diagnostics.Stopwatch();
                sp2.Start();

                await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
                response.Content = new HttpGZipContent(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);

                byte[] processedBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                _downloadedProcessedBytes += processedBytes.Length;

                sp2.Stop();
                _additionalTimeOverhead += sp2.ElapsedMilliseconds;
            
            else
                _downloadedProcessedBytes += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;

            return response;
        
    

    internal sealed class HttpGZipContent : System.Net.Http.HttpContent
    
        private readonly byte[] _content;
        private readonly System.IO.Compression.CompressionMode _compressionMode;

        public HttpGZipContent(byte[] content, System.IO.Compression.CompressionMode compressionMode)
        
            _compressionMode = compressionMode;
            _content = content;
        

        protected override async System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
        
            if (_compressionMode == System.IO.Compression.CompressionMode.Compress)
            
                using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content.Length))
                
                    using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Compress))
                    
                        zipStream.Write(_content, 0, _content.Length);
                        zipStream.Flush();
                    

                    byte[] compressed = memoryStream.ToArray();
                    System.IO.MemoryStream copyStream = new System.IO.MemoryStream(compressed);
                    await copyStream.CopyToAsync(stream).ConfigureAwait(false);
                
            
            else
            
                using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content, 0, _content.Length))
                
                    using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Decompress))
                    
                        await zipStream.CopyToAsync(stream).ConfigureAwait(false);
                    
                
            
        

        protected override bool TryComputeLength(out long length)
        
            length = _content.Length;
            return true;
        

        protected override void Dispose(bool disposing)
        
            base.Dispose(disposing);
        
    

【问题讨论】:

【参考方案1】:

根据我的理解,您需要为您的移动应用后端实现请求解压。如果您使用的是 C# 后端,您可以创建自定义 ActionFilterAttribute,如下所示:

public class RequestDeCompressFilter : ActionFilterAttribute

    public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    
        var request = actionContext.Request;
        if (request.Content.Headers.ContentEncoding.Contains("GZIP"))
        
            await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
            request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
        
       //TODO: compress the response, you could follow http://www.intstrings.com/ramivemula/articles/jumpstart-47-gzipdeflate-compression-in-asp-net-mvc-application/
       await base.OnActionExecutingAsync(actionContext, cancellationToken);
    

    public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
       
        //you could also compress the response here
        return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
    

然后,将您的操作标记如下:

[RequestDeCompressFilter]
public async Task<IHttpActionResult> PostMessage(Message item)

另外,您可以按照 HTTP Message Handlers in ASP.NET Web API 来实现您的 HTTP 消息处理程序。

【讨论】:

以上是关于Azure 应用服务 - 请求中的 GZip 压缩的主要内容,如果未能解决你的问题,请参考以下文章

gzip压缩实践

httpwebrequest 请求压缩,接受压缩的字符流请求

IOS 网络请求GZIP压缩

Okhttp3请求网络开启Gzip压缩

前端打包gzip + nginx开启静态gzip

ServiceStack - 对 JSONP 请求使用 gzip/deflate 压缩