Monday, September 8, 2008

Zip XML in memory for Web Service transport (SharpZipLib)

Here is a full test program that demonstrates how to use SharpZipLib to zip an XElement into a byte array. This allows you to transfer large XML items over web services, and then unzip then on the web service side. I included two methods to unzip, both back to an XElement and to an XML file. IIS 6 does allow compression as well, but the reason I had to have the functionality below was that a PC client application was required to send a host web service a large set of XML (rather than the host sending the client XML).

using System;
using System.IO;
using System.Xml.Linq;
using ICSharpCode.SharpZipLib.Zip;

namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
new Program().Run(args);
}

private void Run(string[] args)
{
// create some xml
XElement xml = XElement.Parse("<xml><element>whatever</element></xml>");

// zip xml
string startXml = xml.ToString();
byte[] bytes = ZipContent(xml, "TestXML");

// unzip xml
xml = UnzipContent(bytes);
string endXml = xml.ToString();

// sanity check
System.Diagnostics.Debug.Assert(startXml == endXml);
}

/// <summary>
/// Convert XML to zipped byte array.
/// </summary>
/// <param name="xml">XML to zip.</param>
/// <param name="entryName">The zip entry name.</param>
/// <returns>A byte array that contains the xml zipped.</returns>
private byte[] ZipContent(XElement xml, string entryName)
{
// remove whitespace from xml and convert to byte array
byte[] normalBytes;
using (StringWriter writer = new StringWriter())
{
xml.Save(writer, SaveOptions.DisableFormatting);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
normalBytes = encoding.GetBytes(writer.ToString());
}

// zip into new, zipped, byte array
using (Stream memOutput = new MemoryStream())
using (ZipOutputStream zipOutput = new ZipOutputStream(memOutput))
{
zipOutput.SetLevel(9);

ZipEntry entry = new ZipEntry(entryName);
entry.DateTime = DateTime.Now;
zipOutput.PutNextEntry(entry);

zipOutput.Write(normalBytes, 0, normalBytes.Length);
zipOutput.Finish();

byte[] newBytes = new byte[memOutput.Length];
memOutput.Seek(0, SeekOrigin.Begin);
memOutput.Read(newBytes, 0, newBytes.Length);

zipOutput.Close();

return newBytes;
}
}

/// <summary>
/// Return zipped bytes as unzipped XML.
/// </summary>
/// <param name="bytes">Zipped content.</param>
/// <returns>Unzipped XML.</returns>
private XElement UnzipContent(byte[] bytes)
{
// unzip bytes into unzipped byte array
using (Stream memInput = new MemoryStream(bytes))
using (ZipInputStream input = new ZipInputStream(memInput))
{
ZipEntry entry = input.GetNextEntry();

byte[] newBytes = new byte[entry.Size];
int count = input.Read(newBytes, 0, newBytes.Length);
if (count != entry.Size)
{
throw new Exception("Invalid read: " + count);
}

// convert bytes to string, then to xml
string xmlString = System.Text.ASCIIEncoding.ASCII.GetString(newBytes);
return XElement.Parse(xmlString);
}
}

/// <summary>
/// Save zipped bytes as unzipped file.
/// </summary>
/// <param name="bytes">Zipped content.</param>
/// <param name="path">File path to save unzipped XML.</param>
private void UnzipContent(byte[] bytes, string path)
{
// unzip bytes into unzipped byte array
using (Stream memInput = new MemoryStream(bytes))
using (ZipInputStream zipInput = new ZipInputStream(memInput))
using (BinaryWriter writer = new BinaryWriter(File.Create(path)))
{
ZipEntry entry = zipInput.GetNextEntry();

int count;
byte[] input = new byte[1024 * 10];
while ((count = zipInput.Read(input, 0, input.Length)) > 0)
{
writer.Write(input, 0, count);
}
}
}
}
}

No comments:

Can't RDP? How to enable / disable virtual machine firewall for Azure VM

Oh no!  I accidentally blocked the RDP port on an Azure virtual machine which resulted in not being able to log into the VM anymore.  I did ...