Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using XML CDATA nodes to send files via a Web Service

0.00/5 (No votes)
26 Jul 2005 2  
Using XML CDATA nodes to send files via a web service.

Introduction

There was a need to have a web service send a file. The documented way to do this is through WSE (Web Service Extension), which is an add-on to the .NET Framework to do things with web services like attachments. Using two CodeProject articles, I added zipping the file and encoding the file using Base64. Then I placed the encoded file into a CDATA node in an XML document.

Background

As I looked into sending a file via a web service, I was not pleased with the amount of work that had to be done to attach a file using WSE. So I started to look for other options. One of my co-workers suggested using a CDATA node in an XML document. As I looked into the CDATA node, it seemed like it might be a good fit. The CDATA node is for storing blob data in an XML file. It is marked so that it ignores what is between the CDATA nodes. I decided to zip the file to reduce the size of the XML file I would be sending. The problem is that zipping a file produces some characters that cause problems with the XML file. So, that leads us to base64 encoding. I found two code project articles to help me with zipping files with Sharpziplib and base64 encoding that would fit into what I needed.

The code

After having some discussion with my co-workers, we decided on an XML structure that looks like this:

<FILES>
  <FILE FileName="file.txt"><CDATA></CDATA></File>
  <FILE FileName="file2.txt"><CDATA></CDATA></File>
</FILES>

where we could add as many file nodes in the XML document as we wanted. Here is the code to create that XML doc:

public static XmlDocument GetFileXml(String[] Files)
{
  //We are returning an XML Doc

  XmlDocument tmpXmlDoc = new XmlDocument();

  //Create the root node...

  tmpXmlDoc.LoadXml("<FILES></FILES>");

  //Loop through the WorkItemFiles 

  //array and create the file nodes

  for (Int32 i=0;i<=Files.Length-1;i++)
  {
    //The File node

    XmlElement tmpFile = 
          tmpXmlDoc.CreateElement("File");
    //Add the filename attribute to it...

    XmlAttribute tmpA = 
          tmpXmlDoc.CreateAttribute("FileName");
    //Find the last \ to pull off the filename.

    tmpA.Value = 
        Files[i].Substring(Files[i].LastIndexOf(@"\")+1);
    tmpFile.Attributes.Append(tmpA);

    //Read in the File

    Byte[] myCompressed;

    //Compress the file

    myCompressed = 
        CompressionHelper.CompressFile(Files[i]);

        //Next encode the compressed stream 

        //so that we don't blow the 

        //CData node in the XML

    Char[] myEncoded;
    Base64Encoder myBE;
    myBE = new Base64Encoder(myCompressed);
    myEncoded = myBE.GetEncoded();

    //A StringBuilder is the fastest way 

    //to get a string from a byte array

    StringBuilder myStr = new StringBuilder();
    myStr.Append(myEncoded);

    //create CData child appened 

    //to the file node...

    tmpFile.AppendChild(tmpXmlDoc.CreateCDataSection(
                                       myStr.ToString()));

    //Add the file node to the xml doc...

    tmpXmlDoc.DocumentElement.AppendChild(tmpFile);
  } // for


  return tmpXmlDoc;
}

Next, we call the web service to get the XML document. Then, we parse through the XML file and extract and save the documents from the CDATA nodes.

public static void GetFiles(XmlNode inXml, String FilePath)
{
  XmlNodeList myFile;

  myFile = inXml.SelectNodes("/File");

  //This Loops through the File nodes

  for (Int32 j=0;j<=myFile.Count-1;j++)
  {
    //The first child is the CDATA node

    XmlNode myCData = (XmlNode)(myFile.Item(j)).FirstChild;

    //Grab the Filename attribute

    String FileName = 
      ((XmlNode)(myFile.Item(j))).Attributes.Item(0).InnerText;

    //This is the actual data that is in the CDATA

    Char[] theFile;
    theFile = new Char[myCData.InnerText.Length];

    for (Int32 i=0;i<=myCData.InnerText.Length-1;i++)
    {
        theFile[i] = myCData.InnerText[i];

    }//for i

    //Delete the file if it exists

    if (File.Exists(FilePath+FileName))
    {
        File.Delete(FilePath+FileName);
    }

    //next we need to switch from base64 

    //encoding to our zip compressed

    Byte[] myDecoder;
    Base64Decoder myBD = new Base64Decoder(theFile);
    myDecoder = myBD.GetDecoded();

    //Finally we decompress the file and 

    //save it where it is supose to go

    CompressionHelper.DecompressFile(myDecoder,
                                  FilePath+FileName);


  } //for j

}

Conclusion

I would like to thank my co-worker Steve Rowe for his help and guidance in coming to this solution. I would also like to thank Uwe Keim for his shareziplib article and wchvic for his base64 article. Hopefully, others will find this article helpful in their solutions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here