Sharepoint 2013 REST API: The C# Connection: Part 4 Document Libraries, Folders And Files   4 comments

The Document Library is a list like all things within Sharepoint, but it is a list with a difference. Each list item within a document library is associated with one Document.  To access the document through the user agent (the browser) we can address the item “directly” using the simple URI:

https://{server name}/{document library name}\{folder name(s)}\{document name}.{extension}

Note: I will use the convention of using the brackets {} to indicate parameters which are supplied when you instantiate a line of code.  Do NOT include the brackets in your code.

This will conjure up the document and launch an appropriate viewer application (Adobe Adobe, MS Word or what have you).  This type of addressing will not work when we are using the REST API for uploading or downloading a document. Although we ignored the issue of folders within lists in our discussions so far we need to address the issue here.  Keep in mind however that folders can appear in normal (non-Document Library) lists also.  I will defer didscussion of creating folders and deletes of folders and documents for a subsequent post.  In this post let’s work on the downloading documents  and then look at how to upload documents.  As with all things Sharepoint, it doesn’t work how you might think.

Document Library Item Details

If we use the REST API and retrieve a list all items within a document library in the normal manner (see Part II for details) our collection of  entry elements looks normal each entry has a collection of link nodes and (within the content node) a collection of property nodes.  Out of the box the properties seem straight forward.

Document Library Item Node

Note that the Document associated with this item does NOT appear in the list of properties.  But we can get that with a second REST API call.  If we expand the d:FileSystemObjectType properties for the item pictured above (which is a normal item) we see that its value is 0.  If the Library has folders then the folder is also listed as an entry in the entry collection along with document items.  Here is how one such entry item for a folder looks:

Folder2 Item

Looks the same right?  If we expand the d:FileSystemObjectType its value is 1. The “documentation” for FileSystemObjectType is obsure and indicates that this property is “An enumeration value that indicates the type: file, folder, Web, or invalid”.  So apparently 1 indicates a folder item while our observed value of “0” indicates a file item. Or something.  Don’t be confused by the title on this item, the name I gave to this folder is Folder0.0. Moving on, having an item which references a file (Document) we can make a call using the link “FieldValuesAsText” we can get a second item entry with the actual document reference:

FileItemAsText

Here we have the file name (d:fileLeafRef) the Document Library (d:fileDirRef) and the relative path for this item (d:FileRef).  It is the later value with we get when we use the Publication Hyperlink field in a List Item.  Note that d:FileRef will include any folders that the document is nested within. In this case the PDF file called “Screen clipping taken 9152013.pdf” is in the root of the Document Library “DLR Document Library”.

Document Down Load

In order to download a file we need to retrieve the binary byte stream for the document using the REST API.  Here is the basic idea.

Build a  basic uri fragment on this format:

web/GetFolderByServerRelativeUrl(‘/{path}’)/Files/$value?$filter=Name eq ‘{document name}’

where:

{path} is the Document Library and path found in d:fileRef excluding the document and extension.

{document} is the value found in d:fileLeafRef (document Name plus extension)

The $value directs Sharepoint to prove the binary bytes for this document.

As an alternative we can form a uri fragment in this form:

web/ TO DO ADD THIS LINE

Which is the value of the “EDIT” link from the links collection of the document library item and appending

/$value

Now call a REST API get against this uri fragment using a HTTPClient prepared as discussed in Part I with the addition of a header as follows:

client.DefaultRequestHeaders.Add(“binaryStringResponseBody”, “true”);

//The header tells Sharepoint to give us an uncontaminated binary stream

HttpResponseMessage resp = client.GetAsync(uri).Result;

Now we need to read the response body (which is binary format) into a binary MemoryStream:

MemoryStream ms=new MemoryStream();
resp.Content.CopyToAsync(ms);
ms.Position = 0;

This memory stream can be passed to a file write routine to persist the document to disk or in our case can be used to upload a document to a different Sharepoint site.

Document Upload

To upload a document into a Document Library the procedure is the same but different.  The Tasks are:

  • Get the document into a  MemoryStream object;
  • Prepare an HTTP Client Object for an HTTP Post
  • Get A Digest Object From Sharepoint using a REST API Post
    • Code Method: RetrieveDigest & RESTPost
    • The LINQ code to extract the Digest value from the response is given in the ctor method of the class:  CDigest
  • Prepare a Posting URI fragment
  • Prepare A uri fragment to use to POST the binary data to the Document Library
  • Prepare an HTTP Client Object for an second HTTP Post
    • Code Method: GetHTTPClient
    • Code Method: _PutBytes & RESTPostBinary
      • Set a Header containing the Digest Object
      • Prepare a Response Content  with the binary data from the memory stream
      • Set a Response Header with the proper Media Type on the Response Content Object
      • Set a Response Header telling Sharepoint you are sending a binary object in the ‘body’
      • Attach the Content Body to the HttpClient Object
      • Call the Post Command

and “like magic”, your document is created and attached to the Sharepoint like and a file Item is created in the Document library as a side effect of the upload.  Wow.  Lets fill in the details for this process.  I will assume you have created and populated the Memory Stream object with the contents of your binary file.  I will show a full code sample at the end of this post.

Get a Sharepoint Digest Object

In order to modify data within Sharepoint using the REST API you must request a Digest object from Sharepoint and attach this Digest item to your post command.  To get the digest value we must make an HTTP POST call to a special Sharepoint REST API Endpoint:

contextinfo

The actual Digest value is stored in the property field: FormDigestValue and its duration is given in the field: FormDigestTimeoutSeconds.  If your program is long running you may need to get a fresh copy of the Digest to use occasionally.  My code to retrieve a Digest looks something like this:

public static SPObject.CDigest RetrieveDigest(HttpClient client)
{
string respString;
statusCode = RESTPost(client,”contextinfo”);
if (statusCode == HttpStatusCode.OK)
{
return new SPObject.CDigest(respString);
}
throw ApplicationException(“Something Bad Happened”);
}
public static string RESTPost(System.Net.Http.HttpClient client, string uri)
{
var resp = client.PostAsync(uri, new StringContent(string.Empty)).Result;
respString = resp.Content.ReadAsStringAsync().Result;

            if (resp.StatusCode!=HttpStatus.OK){

                 throw new ApplicationException(“Something Bad Happened”);

            }
return respString;
}

public class SPObject
{
public class CDigest
{
public string Value { get; set; }
public string TimeOutSeconds { get; set; }
public System.DateTime GetTime { get; set; }
public CDigest() { }
public CDigest(string respString)
{
XDocument xResponse = XDocument.Parse(respString);
IEnumerable digestList = from g in

                     xResponse.Descendants(CSPNamespace.dataServicesNS + “GetContextWebInformation”)
select new CDigest
{
Value = g.Elements(CSPNamespace.dataServicesNS + 

                                                    “FormDigestValue”).First().Value.ToString(),
TimeOutSeconds = g.Elements(CSPNamespace.dataServicesNS +

                                                    “FormDigestTimeoutSeconds”).First().Value.ToString(),
GetTime = System.DateTime.Now

                                                  };
CDigest digest = digestList.First();
Value = digest.Value;
TimeOutSeconds = digest.TimeOutSeconds;
GetTime = digest.GetTime;
}
}
}

Uploading The Binary Data with an HTTP POST

Now that we have a Digest object we need to compose our uri to post the data as a binary byte stream into the document library.  According to this Microsoft Documentation  (11/1/2013) the basic uri fragment is composed from this form:

web/GetFolderByServerRelativeUrl(‘{path}’/Files/add(url='{document}’,overwrite={true | false})

Where:

{path} is the Document Library and any folders.

{document} is the document and its extension.

For example for a document yourClaims.PDF within the Claims folder within the document library called My Library and we allow file overwrites,  the uri fragment becomes:

web/GetFolderByServerRelativeUrl(‘My Library/Claims/’/Files/add(url=’yourClaims.PDF’,overwrite=true)

Now we need to add the Digest Value to a Header of the HTTPClient object:

client.DefaultRequestHeaders.Add(“X-RequestDigest”, {digest values as an unqouted string});

Ok so far so good.  You can see my code example below in the method _PutBytes.  In order to POST data we need to prepare a  response body.  In C# we do this with the special object: HttpContent as:

HttpContent reqContent = new StreamContent(ms);

where ms is the open memorystream of the binary bytes of our document to post. We need to add two specialized headers to this object before we post:

reqContent.Headers.Add(“binaryStringRequestBody”, “true”);
reqContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(“application/json;odata=verbose”);

Now we are ready to use the HTTPClient PostAsync method to POST our document to the server.  See my method RESTPOSTBinary below for a coding example.

void _PutBytes

( CENTRYTranslate data, MemoryStream ms, HttpClient clientWriter,string fullpath, string documentName)
{
SPObject.CDigest digest = AIC.Http.Client.CHttpObject.RetrieveDigest(clientWriter, out statusCode);
string URLCreateDocument =

“web/GetFolderByServerRelativeUrl(‘{path}’/Files/add(url='{document}’,overwrite={true | false})”

           clientWriter.DefaultRequestHeaders.Add(“X-RequestDigest”, digest.Value);
string respString = CHttpObject.RESTPostBinary(clientWriter, URLCreateDocument, ms);

            if (statusCode != HttpStatusCode.OK)
{
throw ApplicationException(“Something Bad Happened”);

              }
}

public static string RESTPostBinary(System.Net.Http.HttpClient client, string uri,MemoryStream ms)
{

         HttpContent reqContent = new StreamContent(ms);
reqContent.Headers.Add(AIC.SP.REST.CRESTAPIEndPoints.MSXBINARYREQUESTBODYHEADER,  CStaticClass.HEADERTRUESTRING);
reqContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(CStaticClass.CONTENTTYPEHEADERJSON);
var resp = client.PostAsync(uri, reqContent).Result;
If (resp.status!=HTTPStatus.OK){

              throw new ApplicationException(“SOMETHING BAD HAPPENDED”);

             }

         return resp.Content.ReadAsStringAsync().Result;
}

Easy and fun, no?

Sharepoint 2013 REST API: The C# Connection: Part 1 Using System.Net.Http.HttpClient

Sharepoint 2013 REST API: The C# Connection: Part 2 Query List or Item and Decoding The Meta-Data

Sharepoint 2013 REST API: The C# Connection: Part 3 Working With List Item Data

Sharepoint 2013 REST API: The C# Connection: Part 4 Document Libraries, Folders And Files
Sharepoint 2013 REST API: The C# Connection: Part 5 REST API More on Folders and Other Odds & Ends

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: