如何在C#中使用System.Net.WebClient上传GZip压缩数据

本文关键字:WebClient Net 上传 GZip 数据 压缩 System | 更新日期: 2023-09-27 18:08:12

在我的项目中,我需要从桌面应用程序上传大型JSON数据。所以,我需要压缩它。

我到处找,但没有找到解决问题的复杂方法。所以,我把几个片段放在一起。请参阅下面的答案。我希望你觉得它有用。

如何在C#中使用System.Net.WebClient上传GZip压缩数据

我扩展了WebClient并阴影其UploadString方法(所有重载(:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
namespace DesktopApp
{
    public class ExtendedWebClient : WebClient
    {
        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest w = base.GetWebRequest(uri);
            w.Timeout = 60 * 60 * 1000;
            return w;
        }
        private byte[] GZipBytes(string data)
        {
            //Transform string into byte[]  
            byte[] ret = null;
            using (System.IO.MemoryStream outputStream = new System.IO.MemoryStream())
            {
                using (GZipStream gzip = new GZipStream(outputStream, System.IO.Compression.CompressionMode.Compress))
                {
                    //write to gzipper
                    StreamWriter writer = new StreamWriter(gzip);
                    writer.Write(data);
                    writer.Flush();
                    //write to output stream
                    gzip.Flush();
                    gzip.Close();
                    ret = outputStream.ToArray();
                }
            }
            return ret;
        }
        /// <summary>
        /// Overriden method using GZip compressed data upload.
        /// </summary>
        /// <param name="address">Remote server address.</param>
        /// <param name="data">String data.</param>
        /// <returns>Server response string.</returns>
        public new string UploadString(string address, string data)
        {
            string ret = null;
            byte[] bytes = GZipBytes(data);
            this.Headers.Add("content-encoding", "gzip");
            bytes = base.UploadData(address, bytes);
            ret = System.Text.Encoding.UTF8.GetString(bytes);
            return ret;
        }
        /// <summary>
        /// Overriden method using GZip compressed data upload.
        /// </summary>
        /// <param name="address">Remote server URI.</param>
        /// <param name="data">String data.</param>
        /// <returns>Server response string.</returns>
        public new string UploadString(Uri address, string data)
        {
            string ret = null;
            byte[] bytes = GZipBytes(data);
            this.Headers.Add("content-encoding", "gzip");
            bytes = base.UploadData(address, bytes);
            ret = System.Text.Encoding.UTF8.GetString(bytes);
            return ret;
        }
        /// <summary>
        /// Overriden method using GZip compressed data upload.
        /// </summary>
        /// <param name="address">Remote server address.</param>
        /// <param name="method">HTTP method (e.g. POST, PUT, DELETE, GET).</param>
        /// <param name="data">String data.</param>
        /// <returns>Server response string.</returns>
        public new string UploadString(string address, string method, string data)
        {
            string ret = null;
            byte[] bytes = GZipBytes(data);
            this.Headers.Add("content-encoding", "gzip");
            bytes = base.UploadData(address, method,bytes);
            ret = System.Text.Encoding.UTF8.GetString(bytes);
            return ret;
        }

        /// <summary>
        /// Overriden method using GZip compressed data upload.
        /// </summary>
        /// <param name="address">Remote server URI.</param>
        /// <param name="method">HTTP method (e.g. POST, PUT, DELETE, GET).</param>
        /// <param name="data">String data.</param>
        /// <returns>Server response string.</returns>
        public new string UploadString(Uri address, string method, string data)
        {
            string ret = null;
            byte[] bytes = GZipBytes(data);
            this.Headers.Add("content-encoding", "gzip");
            bytes = base.UploadData(address, method, bytes);
            ret = System.Text.Encoding.UTF8.GetString(bytes);
            return ret;
        }
    }
}

你可以像一样使用ExtendedWebClient

        using (ExtendedWebClient client = new ExtendedWebClient())
        {
            try
            {
                //requestData object represents any data you need to send to server
                string data = JsonConvert.SerializeObject(
                                requestData,
                                new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() });
                client.Headers.Add("Content-Type", "application/json; charset=utf-8");
                client.Encoding = System.Text.Encoding.UTF8;
                string url = "http://yourdomain.com/api";
                string response = client.UploadString(url, data);
                //Deal with response as you need
            }
            catch (Exception ex)
            {
                Console.Error.Write(ex.Message);
            }
        }

在服务器上,GZIP上传通常不受支持,所以我需要添加对此的支持。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace WebServer
{
    public class CompressedRequestHandler : DelegatingHandler
    {
        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (IsRequetCompressed(request))
            {
                request.Content = DecompressRequestContent(request);
            }
            return base.SendAsync(request, cancellationToken);
        }
        private bool IsRequetCompressed(HttpRequestMessage request)
        {
            if (request.Content.Headers.ContentEncoding != null &&
                request.Content.Headers.ContentEncoding.Contains("gzip"))
            {
                return true;
            }
            return false;
        }
        private HttpContent DecompressRequestContent(HttpRequestMessage request)
        {
            // Read in the input stream, then decompress in to the outputstream.
            // Doing this asynronously, but not really required at this point
            // since we end up waiting on it right after this.
            MemoryStream outputStream = new MemoryStream();
            Task task = request.Content.ReadAsStreamAsync().ContinueWith(t =>
                {
                    Stream inputStream = t.Result;
                    using (GZipStream gzipStream = new GZipStream(inputStream, CompressionMode.Decompress))
                    {
                        gzipStream.CopyTo(outputStream);
                    }
                    // Setting output streem position to begin is ESSENTIAL!
                    outputStream.Seek(0, SeekOrigin.Begin);
                });
            // Wait for inputstream and decompression to complete. Would be nice
            // to not block here and work async when ready instead, but I couldn't 
            // figure out how to do it in context of a DelegatingHandler.
            task.Wait();
            // Save the original content
            HttpContent origContent = request.Content;
            // Replace request content with the newly decompressed stream
            HttpContent newContent = new StreamContent(outputStream);
            // Copy all headers from original content in to new one
            // Change content-encoding and content-length
            foreach (var header in origContent.Headers)
            {
                // Change content-encoding header to default value
                if (header.Key.ToLowerInvariant() == "content-encoding")
                {
                    newContent.Headers.Add(header.Key, "identity");
                    continue;
                }
                // Change content-length header value to decompressed length
                if (header.Key.ToLowerInvariant() == "content-length")
                {
                    newContent.Headers.Add(header.Key, outputStream.Length.ToString());
                    continue;
                }
                // Copy other headers
                newContent.Headers.Add(header.Key, header.Value);
            }
            ////For testing purpose only!
            //Task task2 = newContent.ReadAsStringAsync().ContinueWith(x =>
            //{
            //    string strConent = x.Result;
            //});
            //task2.Wait();

            return newContent;
        }
    }
}

此处理程序必须在GlobalConfig文件中注册(在App_Start下(:

using System.Web.Http;
using Newtonsoft.Json.Serialization;
using System.Web.Http.Filters;
namespace WebServer
{
    public static class GlobalConfig
    {
        public static void CustomizeConfig(HttpConfiguration config)
        {
            //REGISTER CompressedRequestHandler
            config.MessageHandlers.Add(new CompressedRequestHandler());
            // Remove Xml formatters. This means when we visit an endpoint from a browser,
            // Instead of returning Xml, it will return Json.
            // More information from Dave Ward: http://jpapa.me/P4vdx6
            config.Formatters.Remove(config.Formatters.XmlFormatter);
            // Configure json camelCasing per the following post: http://jpapa.me/NqC2HH
            // Here we configure it to write JSON property names with camel casing
            // without changing our server-side data model:
            //var json = config.Formatters.JsonFormatter;
            //json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            //json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            //remove standard JSON formatter
            config.Formatters.Remove(config.Formatters.JsonFormatter);
            //add JSONP formatter to support both JSON and JSONP
            var jsonp = new JsonpMediaTypeFormatter();
            jsonp.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            jsonp.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            config.Formatters.Add(jsonp);
            // Add model validation, globally
            config.Filters.Add(new ValidationActionFilter());
            //            config.Filters.Add(new AuthorizeAttribute());
            //
            //config.Filters.Add(new CustomSecurityAttribute());
            //config.Filters.Add(new XchangeSecurityAttribute());
            config.Filters.Add(new ExceptionHandlingAttribute());
        }
    }
}