The main focus of this post is to “extract” the attachments from a submitted InfoPath form and uploading those into a document library and return the links from the documents.
The problem that we were facing was 2 fold:
1. To avoid embedding/attaching any uploaded files to the InfoPath form and upload them directly to SharePoint Library.
2. To bring up-to-date/edit and update the existing form built based on the old functionality so that the existing data is not lost.
Ofcourse this requirement 1 is so generic that I had to be out there on our beloved world wide web 😉 And luckily.. it was.. partly..! I found the following pieces of invaluable information: Upload an InfoPath attachment to a document library by Koen Roos and KB892730: How to encode and decode a file attachment programmatically by using Visual C# in InfoPath. But my blog covers the 2nd part too (partly again).
Another very interesting blog is this(On SpittingCAML) which provides a very good potential solution if your code supports/requires a custom web handler (deployed to the GAC, deployed as a solution in the SharePoint farm).
First things first.. what is Koen talking about in his post? Well he references another post that describes how you can generates classes out of your XML file.
Since all submitted InfoPath forms are purely XML you can generate classes from it as well! So how does it work then?
- Download a copy of a submitted form to your development machine
- Run the Visual Studio Command line
- Execute the following cmd : XSD “name of the submitted form”
- Execute the following cmd : XSD “name of the created XSD file” /CLASSES
- Open Visual Studio and open up your workflow project (see previous posts about how-to-do this)
- Add the just created .cs file to your project
- Then you can use code to get access to the values from the infopath fields in an OO way
System.Xml.Serialization.XmlSerializer xSerializer = new System.Xml.Serialization.XmlSerializer(typeof(myFields)); myFields res = (myFields)xSerializer.Deserialize(str); Console.WriteLine(res.FirstName);
Where MyFields is the main datasource in my InfoPath form, so now when I want to access a particular field within my datasource I can use “res.AttachmentField” or “res.FirstName” to get the values. Now to get the binary data from the attached files I used the code from the KB article that can encode and decode the string and that will look like this :
InfoPathAttachmentDecoder dec = new InfoPathAttachmentDecoder(r.AttachmentField);
Now to put that back into a document library as a document we do the following
SPFolder AttachmentStore = web.GetFolder("Document Library"); AttachmentStore.Files.Add(dec.Filename, dec.DecodedAttachment);
To put it al together it will look like (so you can copy/paste it more conveniently ;))
SPSite site = new SPSite("http://portal/sites/subsite"); SPWeb web = site.OpenWeb(); SPFile file = web.GetFile(@"http://portal/sites/subsite/Document Library/form.xml"); System.IO.StreamReader str = new System.IO.StreamReader(file.OpenBinaryStream()); System.Xml.Serialization.XmlSerializer xSerializer = new System.Xml.Serialization.XmlSerializer(typeof(myFields)); myFields res = (myFields)xSerializer.Deserialize(str); InfoPathAttachmentDecoder dec = new InfoPathAttachmentDecoder(res.AttachmentField); SPFolder AttachmentStore = web.GetFolder("Document Library"); AttachmentStore.Files.Add(dec.Filename, dec.DecodedAttachment);
Now the next step is to make a custom workflow activity using this code.
So using the 2 approaches discussed here, we can cater to both the needs as described above.
I hope this helps you sort out/solve most of you InfoPath related issues 🙂