Transferring attachment file from Web Service to InfoPath

I previously posted about InfoPath and Web Service data connections. Back then I didn't mention that you could use the same method to transfer InfoPath attachments as well. It's actually pretty straightforward thing. All you need to do at the web service is wrap up you existing file data to InfoPath file attachment format and return it as BASE64 encoded. It sounds easy and it really is. Well enough talking... let's checkout the code!

First we need method that creates InfoPath attachment from byte array. It could be something like this:

  1  static public String CreateInfoPathAttachment(String fileName, byte[] fileData)
 2  {
 3      // This memory stream for InfoPath attachment buffer before Base64 encoding.
 4      using (MemoryStream ms = new MemoryStream())
 5      {
 6          uint fileNameLength = (uint)fileName.Length + 1;
 7          byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName);
 8
 9          using (BinaryWriter bw = new BinaryWriter(ms))
10          {
11              // Write the InfoPath attachment signature. 
12              bw.Write(new byte[] { 0xC7, 0x49, 0x46, 0x41 });
13
14              // Write the default header information.
15              bw.Write((uint)0x14); // size
16              bw.Write((uint)0x01); // version
17              bw.Write((uint)0x00); // reserved
18
19              // Write the file size.
20              bw.Write((uint)fileData.Length);
21  
22              // Write the size of the file name.
23              bw.Write((uint)fileNameLength);
24  
25              // Write the file name (Unicode encoded).
26              bw.Write(fileNameBytes);
27  
28              // Write the file name terminator. This is two nulls in Unicode.
29              bw.Write(new byte[] { 0, 0 });
30              bw.Write(fileData);
31          }
32          return Convert.ToBase64String(ms.ToArray());
33      }
34  }

This code has been "copy-pasted" mainly from this source. However there is unnecessary code (=my opinion) so I have changed it a little bit. I prefer using the existing ToBase64String method (unlike in original code sample).

Now all you need to do at you web service is to pass filename and data to the method and it creates BASE64 encoded string that can be returned to the InfoPath.

 1  ...
2  String fileName;
3  DocumentContainer myDocument;
4  ...
5  byte[] fileData = File.ReadAllBytes(fileName);
6  myDocument.Size = fileData.Length;
7  myDocument.Document = CreateInfoPathAttachment(fileName, fileData);
8  return myDocument;

In my example I use similar approach as in my previous example. I have Document struct that has (in this case) at least to fields: size and document. And in InfoPath I could map those fields exactly as I explained in my earlier post. Now you just map Document field to your InfoPath attachment and that's it:
 
And what makes this really nice... it works also with Forms Services as well! Of course this idea could be also otherway around... so you could sent file from InfoPath to web service using the same underlying idea. In that case you should only parse the InfoPath attachment so that you get the original filename and filedata. Read more about this case in here.

Anyways... Happy hacking!

J