رادکام
فشرده سازی پیام ها هنگام استفاده WebService هایکی از مشکلاتی که در هنگام استفاده از وب سرویس ها ممکن است به آن بر بخورید، حجم زیاد اطلاعاتی است که باید رد و بدل شود و گهگاه کاربر را در استفاده از متدهای یک WebService، دچار مشکل می کند. از آنجایی که XML زبان خلاصه گویی نیست و برای بیان اطلاعات Tag های فراوان دارد، حجم اطلاعات رد و بدل شده زیاد است. در این مقاله راه حلی را برای ZIP کردن اطلاعات قبل از ارسال و باز کردن آنها پس از دریافت را شرح می دهیم. امید است که مورد استفاده قرار گیرد. پیش نیاز هااولین سؤالی که ممکن است پیش آید این است که از کدام الگوریتم برای ZIP کردن استفاده شود؟ با توجه به اینکه C# هیچ کلاسی برای این کار ندارد می توانید از توابع و کتابخانه های آماده، استفاده کنید. یکی از کتابخانه های معروف #ZipLib است که به صورت OpenSource موجود است و می توانید آن از آدرس زیر Download کنید. http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx در راه حل ارائه شده ، لازم نیست که تمام پیام فشرده شود و فقط محتویات پیام را فشرده شده و ارسال می شود و پس از دریافت، فقط محتویات پیام باز می شوند. پیاده سازیدو کلاس باید پیاده سازی شوند، یکی باید SoapExtention را توسعه دهد و کلاس دیگر SoapExtensionAttribute. CompressionExtensionدر کلاس CompressionExtension دو مرحله وجود دارد. یکی AfterSerialize و دیگر BeforeDeserialize. در AfterSerialize اطلاعات Zip می شوند و اطلاعات فشرده شده در پیام قرار داده شده و ارسال می شود. در مرحله BeforeDeserialize برعکس مرحله بالا انجام می شود، بدین صورت که اطلاعات بررسی شده، UnZip می شوند و اطلاعات بازشده در پیام قرار داده می شود تا آماده استفاده باشد. وقتی اطلاعات موجود در پیام فشرده می شود، اطلاعات به آرایه ای از اطلاعات
دودویی تبدیل می شود. امکان نمایش اطلاعات
دودویی در در پیام ارسالی ممکن
نیست، و باید اطلاعات را با الگوریتم BASE64 تبدیل کنیم. |
using System; using System.IO; using System.Text ; using System.Web.Services; using System.Web.Services.Protocols ; using ICSharpCode.SharpZipLib.Checksums; using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.GZip; using System.Xml ;
namespace Radcom { /// <summary> /// Summary description for ConpressionExtension. /// </summary> public class CompressionExtension : System.Web.Services.Protocols.SoapExtension { Stream oldStream; Stream newStream;
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return attribute; }
// Get the Type public override object GetInitializer(Type t) { return typeof(CompressionExtension); }
// Get the CompressionExtensionAttribute public override void Initialize(object initializer) { CompressionExtensionAttribute attribute = (CompressionExtensionAttribute) initializer;
return; }
// Process the SOAP Message public override void ProcessMessage(SoapMessage message) { // Check for the various SOAP Message Stages switch (message.Stage) {
case SoapMessageStage.BeforeSerialize: break;
case SoapMessageStage.AfterSerialize: // ZIP the contents of the SOAP Body after it has // been serialized Zip(); break;
case SoapMessageStage.BeforeDeserialize: // Unzip the contents of the SOAP Body before it is // deserialized Unzip(); break;
case SoapMessageStage.AfterDeserialize: break;
default: throw new Exception("invalid stage"); } }
// Gives us the ability to get hold of the RAW SOAP message public override Stream ChainStream( Stream stream ) { oldStream = stream; newStream = new MemoryStream(); return newStream; }
// Utility method to copy streams void Copy(Stream from, Stream to) { TextReader reader = new StreamReader(from); TextWriter writer = new StreamWriter(to); writer.WriteLine(reader.ReadToEnd()); writer.Flush(); }
// Zip the SOAP Body private void Zip() { newStream.Position = 0; // Zip the SOAP Body newStream = ZipSoap(newStream); // Copy the streams Copy(newStream, oldStream); }
// The actual ZIP method private byte[] Zip(string stringToZip) { byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToZip); MemoryStream ms = new MemoryStream();
// Check the #ziplib docs for more information ZipOutputStream zipOut = new ZipOutputStream( ms ) ; ZipEntry ZipEntry = new ZipEntry("ZippedFile"); zipOut.PutNextEntry(ZipEntry); zipOut.SetLevel(9); zipOut.Write(inputByteArray, 0 , inputByteArray.Length ) ; zipOut.Finish(); zipOut.Close();
// Return the zipped contents return ms.ToArray(); }
// Select and Zip the appropriate parts of the SOAP message public MemoryStream ZipSoap(Stream streamToZip) { streamToZip.Position = 0; // Load a XML Reader XmlTextReader reader = new XmlTextReader(streamToZip); XmlDocument dom = new XmlDocument(); dom.Load(reader); // Load a NamespaceManager to enable XPath selection XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable); nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr); // Select the contents within the method defined in the SOAP body node = node.FirstChild.FirstChild; // Check if there are any nodes selected while( node != null ) { if( node.InnerXml.Length > 0 ) { // Zip the data byte[] outData = Zip(node.InnerXml); // Convert it to Base64 for transfer over the internet node.InnerXml = Convert.ToBase64String(outData) ; } // Move to the next parameter node = node.NextSibling ; } MemoryStream ms = new MemoryStream(); // Save the updated data dom.Save(ms); ms.Position = 0;
return ms; }
// Unzip the SOAP Body &bbsp; private void Unzip() { MemoryStream unzipedStream = new MemoryStream();
TextReader reader = new StreamReader(oldStream); TextWriter writer = new StreamWriter(unzipedStream); writer.WriteLine(reader.ReadToEnd()); writer.Flush(); // Unzip the SOAP Body unzipedStream = UnzipSoap(unzipedStream); // Copy the streams Copy(unzipedStream, newStream);
newStream.Position = 0; }
// Actual Unzip logic private byte[] Unzip(string stringToUnzip) { // Decode the Base64 encoding byte[] inputByteArray = Convert.FromBase64String( stringToUnzip ) ; MemoryStream ms = new MemoryStream(inputByteArray) ; MemoryStream ret = new MemoryStream();
// Refer to #ziplib documentation for more info on this ZipInputStream zipIn = new ZipInputStream(ms); ZipEntry theEntry = zipIn.GetNextEntry(); Byte[] buffer = new Byte[2048] ; int size = 2048; while (true) { size = zipIn.Read(buffer, 0, buffer.Length); if (size > 0) { ret.Write(buffer, 0, size); } else { break; } } return ret.ToArray(); }
// Unzip the SOAP Body public MemoryStream UnzipSoap(Stream streamToUnzip) { streamToUnzip.Position = 0; // Load a XmlReader XmlTextReader reader = new XmlTextReader(streamToUnzip); XmlDocument dom = new XmlDocument(); dom.Load(reader);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable); nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); // Select the SOAP Body node XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr); node = node.FirstChild.FirstChild;
// Check if node exists while( node != null ) { if( node.InnerXml.Length >0 ) { // Send the node's contents to be unziped byte[] outData = Unzip(node.InnerXml); string sTmp = Encoding.UTF8.GetString(outData); node.InnerXml = sTmp; } // Move to the next parameter node = node.NextSibling ; }
MemoryStream ms = new MemoryStream();
dom.Save(ms); ms.Position = 0;
return ms; }
} } |
CompressionExtensionAttributeاین کلاس به شما اجازه می دهد تا توابعی که از خاصیت فشرده سازی استفاده می کنند را از دیگر توابع متمایز کنید. این کلاس از SoapExtensionAttribute ارث بری می کند و مشخصه های ExtensionType و Priority را تغییر می دهد.
|
using System; using System.Web.Services; using System.Web.Services.Protocols;
namespace Radcom { /// <summary> /// Summary description for CompressionExtensionAttribute. /// </summary>
// Make the Attribute only Applicable to Methods [AttributeUsage(AttributeTargets.Method)] public class CompressionExtensionAttribute : System.Web.Services.Protocols.SoapExtensionAttribute {
private int priority;
// Override the base class properties public override Type ExtensionType { get { return typeof(CompressionExtension); } }
public override int Priority { get { return priority; } set { priority = value; } }
} } |
نحوه استفادهپس از کامپایل کردن، کتابخانه شما آماده است. برای استفاده در بخش سرویس دهنده، کافی است Namespace ی که برای پروژه خود درنظرگرفته اید، در قسمت using بیاورید و بعد از تعریف تابع موردنظر خود (پس از [WebMethod]) عبارت [CompressionExtension] را اضافه کنید و تابع موردنظر خود را به صورت عادی و معمول بنویسید. البته بدیهی است که باید DLL های مربوطه (SharpZip , CompressionExtension) را نیز در اختیار داشته باشید تا بتوانید از کتابخانه های توسعه یافته استفاده کنید. نکته ی مهم دیگر اینکه وقتی یک SOAP Call برای یک Web Method به وجود آید، توابع توسعه یافته فعال شده و کار می کنند و اگر برای مراجعه به توابع از HTTP GET / POST استفاده کنید، توابع شما فعال نخواهند شد. برای مثال اگر شما از تست های استانداردی که ASP.NET برای تست وب سرویس در اختیار شما می گذارد استفاده کنید، نتیجه فشرده سازی را نخواهید دید. |
<%@ WebService Language="C#" Class="MyService" %>
using System.Web.Services; using MasterCSharp.WebServices ; using System.Data ; using System.Data.SqlClient ;
public class MyService {
[WebMethod] [CompressionExtension] public string MyMethod() {
// Replace with the connection string to connect to your database DataSet ds = new DataSet(); /* Fill Dataset */ return ds.GetXml() ; }
} |
برای استفاده از توابع در قسمت سرویس گیرنده ، پس از افزودن وب سرویس به عنوان Web Reference باید DLL های موجود را به پروژه خود بیفزایید (هر دو DLL) و سپس در فایل reference.cs در webreference مربوطه قبل از هر تابع عبارت [CompressionExtension] را بیفزایید، چون به طور استاندارد وقتی مرجع را به پروژه خود می افزایید، این مورد وجود ندارد. |
using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; using Radcom;
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Web.Services.WebServiceBindingAttribute (Name="MyServiceSoap", Namespace="http://tempuri.org/")] public class MyService : System.Web.Services.Protocols.SoapHttpClientProtocol {
public MyService() { this.Url = "http://localhost/TestPages/Service.asmx"; }
/// <remarks/> // Add our Custom SOAP Extension [CompressionExtension] [System.Web.Services.Protocols.SoapDocumentMethodAttribute ("http://tempuri.org/MyMethod", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string MyMethod() { object[] results = this.Invoke("MyMethod", new object[0]); return ((string)(results[0])); }
/// <remarks/> public System.IAsyncResult BeginMyMethod(System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("MyMethod", new object[0], callback, asyncState); }
/// <remarks/> public string EndMyMethod(System.IAsyncResult asyncResult) { object[] results = this.EndInvoke(asyncResult); return ((string)(results[0])); } } |
4,889بازدید
دیدگاه کاربران
هنوز دیدگاهی ثبت نشده است.
شما میتوانید درباره این مقاله، دیدگاه خود را ثبت کنید.