Programmatically retrieve an InfoPath form from a SharePoint library

Use the System.Net.WebClient class or Copy web service to retrieve an InfoPath form from a SharePoint library.

Scenario

There may be occassions on which you would like to programmatically retrieve InfoPath forms from within SharePoint without directly accessing the forms by navigating to a document or form library and then clicking on and opening the InfoPath forms.

A few examples would be if you wanted to perform maintenance tasks remotely on InfoPath forms, bulk analyze the contents of all InfoPath forms which are stored within a library, or copy InfoPath forms to another location inside as well as outside of SharePoint, e.g. to the file system or SQL Server.

Solution

You can programmatically retrieve an InfoPath form from a SharePoint library by using either the System.Net.WebClient class or the Copy web service that comes with Windows SharePoint Services 3.0.

Design an InfoPath form to retrieve a list of forms

While you can use either a Console or a Windows application to retrieve InfoPath forms, we are going to use an InfoPath form to retrieve and open other InfoPath forms which are stored within a forms library.

Create a new InfoPath form template with a Receive data data connection that connects to a SharePoint library or list that has a forms library containing InfoPath forms you would like to retrieve. Make sure to include the Title field in the list of fields to retrieve. Call the data connection MyIPForms. Use the (secondary) data source for the data connection to the SharePoint library to design a form template as shown in figure 1. Note that an extra Button control was added to the Repeating Table. This button will be used to open each form in a new instance of InfoPath.


Figure 1. The InfoPath form template in design mode.


Figure 2. The InfoPath form in preview mode.

Now you can write code behind the Open button to retrieve each InfoPath form from the SharePoint library. Two solutions on how to get this done will be presented in this article:

  1. A solution that only works if Forms Server is not installed on the server or if a noredirect=true query string parameter is passed along in the URL to the form.
  2. A solution that always works.

Solution 1: Use the System.Net.WebClient class

The System.Net.WebClient class is good to use when you are only making use of Windows SharePoint Services 3.0 without Forms Server installed. If Forms Server has been installed on top of WSS 3.0, the System.Net.WebClient class will return the contents of the ASP.NET page used by Forms Server to display InfoPath forms instead of the actual XML contents of the form, unless the query string parameter noredirect with a value of true is appended to the URL of the form.

Add the following using statements to the form's code file:

using System.IO;
using System.Net;

Add the following code in the Clicked event handler of the Open button (for WSS without Forms Server):

// Get the connection to the SharePoint library
SharepointListQueryConnection spsConn =
(SharepointListQueryConnection)DataSources["MyIPForms"].QueryConnection;

// Retrieve the name of the form from the current row
string formName = e.Source.CreateNavigator().SelectSingleNode("@Title", NamespaceManager).Value;

// Generate the URL to the form
string formUrl = String.Format("{0}{1}/{2}", spsConn.SiteUrl, spsConn.Name, formName);

// Download the form as a string
WebClient wc = new WebClient();
wc.Credentials = CredentialCache.DefaultCredentials;
string formString = wc.DownloadString(formUrl);

/*
You could use the DownloadData method to download the form
as a byte array and then use that byte array however you'd
like to:
byte[] formData = wc.DownloadData(formUrl);
*/

// Create a temporary file to write the text of the form to
string tempFileName = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFileName, FileMode.Open, FileAccess.ReadWrite))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(formString);
sw.Flush();
sw.Close();
}
fs.Close();
}

// Open the temporary file for the form in a new instance of InfoPath
Application.XmlForms.Open(tempFileName);

Give your form Full Trust and digitally sign it with a certificate.

Now when you click on the Open button for one of the forms, the WebClient class will be used to download that form as a string, create a temporary file for the form, and then open it in a new instance of InfoPath.

Solution 2: Use the Copy web service (recommended)

The Copy web service that comes with Windows SharePoint Services 3.0 is good to use in all situations. Unlike the System.Net.WebClient, the GetItem method of the Copy.asmx web service will always return the properties and XML contents of the InfoPath form requested, whether Forms Server is installed or not.

The Copy web service has 3 web methods:

  1. CopyIntoItems
    Copies a document represented by a byte array stream to a SharePoint server.
  2. CopyIntoItemsLocal
    Copies a document from one location on a Windows SharePoint Services server to another location on the same server.
  3. GetItem
    Generates a byte array stream representation of a document that can be passed to the CopyIntoItems method to copy the document to a different server.

The GetItem web method not only returns a Stream containing a base64 encoded string of the entire form, but also provides metadata information on the form. For example when using mixed content types on a document library, you could use the metadata returned by the GetItem web method to check whether the InternalName field with a value of xd_ProgID has a corresponding Value field with a value of InfoPath.Document.2 to make sure that the file that was retrieved is an InfoPath document.

Create a Receive data data connection to a Web service, enter the URL to the Copy web service on your WSS server

http://<Site>/_vti_bin/Copy.asmx

and select its GetItem web method. Call the data connection GetItem.

Add the following using statements to the form's code file:

using System.IO;
using System.Text;

Add the following code in the Clicked event handler of the Open button:

// Get the connection to the SharePoint library
SharepointListQueryConnection spsConn =
(SharepointListQueryConnection)DataSources["MyIPForms"].QueryConnection;

// Retrieve the name of the form from the current row
string formName = e.Source.CreateNavigator().SelectSingleNode("@Title", NamespaceManager).Value;

// Generate the URL to the form
string formUrl = String.Format("{0}{1}/{2}", spsConn.SiteUrl, spsConn.Name, formName);

// Get an XPathNavigator object to the GetItem method of the Copy web service
XPathNavigator root = DataSources["GetItem"].CreateNavigator();

// Set the Url parameter on the GetItem method
root.SelectSingleNode("//*[local-name()='Url']").SetValue(formUrl);

// Call the web service
DataSources["GetItem"].QueryConnection.Execute();

// Get the byte array stream of the form from the web service response
XPathNavigator formStream = root.SelectSingleNode("//*[local-name() = 'Stream']");

// Convert the base64 encoded string for the form into a byte array
byte[] formData = Convert.FromBase64String(formStream.Value);

/*
Once you've converted the base64 encoded string for the form
into a byte array, you can do whatever you like with that
byte array. Here we will continue converting it to a string and
then create a temporary file of that string so that we can open
the temporary file in InfoPath.
*/

// Convert the form's byte array into a string
string formString = string.Empty;
using (MemoryStream ms = new MemoryStream(formData, false))
{
using (StreamReader sr = new StreamReader(ms, Encoding.UTF8))
{
formString = sr.ReadToEnd();
sr.Close();
}
ms.Close();
}

// Create a temporary file to write the text of the form to
string tempFileName = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFileName, FileMode.Open, FileAccess.ReadWrite))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(formString);
sw.Flush();
sw.Close();
}
fs.Close();
}

// Open the temporary file for the form in a new instance of InfoPath
Application.XmlForms.Open(tempFileName);

Give your form Full Trust and digitally sign it with a certificate.

Now when you click on the Open button for one of the forms, the Copy web service will be used to retrieve that form, convert its stream into a string, create a temporary file for the form, and then open it in a new instance of InfoPath.

Conclusion

In this article you have seen how to programmatically retrieve InfoPath forms from a SharePoint library. While we have opened each form in InfoPath after retrieving it, it is not necessary to do so. Once the form has been retrieved as a byte array, you can do whatever you would like to do with it whether it is copying it to another location, converting it to a string to analyze its contents, or saving it to SQL Server. The possibilities are endless; go out and have fun with it.

 
 Subscribe for updates via RSS or Email

Related InfoPath Articles:





InfoPath 2013 Cookbook: 121 Codeless Recipes for Beginners

InfoPath 2013 Cookbook 2: 121 Codeless Recipes for SharePoint 2013

InfoPath 2010 Cookbook: 101 Codeless Recipes for Beginners

InfoPath 2010 Cookbook 2: 101 Codeless Recipes for SharePoint 2010

InfoPath 2010 Cookbook 3: 101 Code Recipes for C# Developers

InfoPath 2010 Cookbook 4: 101 Code Recipes for VB Developers

InfoPath 2010 Cookbook 5: Integrating InfoPath with Excel and Excel Services