Applies To: SharePoint, VB.NET
In my last post, Using SharePoint RPC to Create Directories, I showed you the basics of using the SharePoint Foundation RPC Protocol. I provided a lot of the plumbing code needed for executing RPC methods and then demonstrated creating multiple directories in a single call.
In this post, I’ll be building on my SPUploader module to show you how to upload documents while setting the metadata all in a single call. You will need all the code in the first post to make this work.
Uploading Documents
Our goal is to upload a document to a library and set its metadata at the same time using the put document RPC method. There are some additional complications with Office docs that I won’t be addressing, but this should get you started for most file types.
The put document method has four parameters we care about: document, put_option, comment, and keep_checked_out.
Document Details
The document parameter is where you will specify the document name and all meta data. For a document named SomeStuff.csv with a title of “Some Stuff” to be uploaded in the Stuff folder of our Junk library the document parameter needs to look like this:
document=[document_name=Junk/Stuff/SomeStuff.csv;meta_info=[vti_title;SW|Some Stuff]]
Here are some utility functions that will help you generate all the information correctly:
Private Function documentDetailString(fileName As String, properties As Dictionary(Of String, Object)) As String Return String.Format("[document_name={0};meta_info=[{1}]]", EncodeString(fileName), EncodeString(propertiesToString(properties))) End Function Private Function propertiesToString(properties As Dictionary(Of String, Object)) As String If properties Is Nothing Then Return String.Empty Dim props As New StringBuilder For Each kvp As KeyValuePair(Of String, Object) In properties If kvp.Value IsNot Nothing Then Dim field As String = kvp.Key If field = "Title" Then field = "vti_title" Dim value As String Dim typeCode As String = "S" Select Case (kvp.Value.GetType().FullName) Case "System.Boolean" typeCode = "B" value = IIf(CType(kvp.Value, Boolean), "true", "false") Case "System.DateTime" value = CType(kvp.Value, DateTime).ToString("s") & "Z" Case Else value = escapeVectorCharacters(kvp.Value.ToString) End Select props.AppendFormat("{0};{1}W|{2};", field, typeCode, value) End If Next Return props.ToString.TrimEnd(";") End Function
The first function, documentDetailString (lines 96-98), will help generate the basic structure of the document parameter’s value. It uses the EncodeString function from my last post to escape both the file name and all the metadata information between the brackets. When we use this later in an RPCParameter object you’ll see that we disable additional encoding since RPC is really finicky about this stuff.
The metadata information for a file is generated using the propertiesToString function (lines 100-124) and expects your metadata to be in a dictionary object where the internal column name is the key (string) and the value is the value (object). We loop through each property and build a strange looking string that indicates which column we are setting, the type of the value (and what action we want performed), and the value itself.
Generally, every property key needs to be set to the internal name of the column (not the display name). However, the Title column is a special case. When you specify Title it actually needs to be vti_title. This is taken care of in lines 105-106.
The meta_info string for each property has two parts separated by semicolons. The first is the internal name of the column, the second is information about the value. This second part also has two parts separated by a pipe. The first part will be two characters. The first character indicates the type of the value (S for everything but Boolean values where you will use B). The second character will always be W (write) and specifies the action to take.
We take care of this with a simple case statement that checks the type of the value (line 110). When it’s a Boolean, we use a B for the type character and set the value to the string true or false (lines 111-113). When it’s a DateTime, we still use S for the type character but we format the value into the standard Sortable format and slap a Z on the end (lines 114-115). For everything else, we specify S for type and use our escapeVectorCharacters method to escape it properly (lines 116-117).
Once we’ve looped through all the properties, we remove the final semicolon (line 123).
Unique File Names
Here’s a freebie function should you need it:
Public Function UniqueFileName(originalFileName As String) As String Return "C" & DateTime.Now.ToString("yyMMddHHmmssfffffff") & "K" & Path.GetExtension(originalFileName) End Function
The UniqueFileName function above is a quick and dirty way to ensure that you aren’t overwriting your documents when uploading them in bulk.
Uploading
Public Function UploadToSharePoint(webURL As String, libraryName As String, FolderPath As String, FileName As String, fileBytes As Byte(), properties As Dictionary(Of String, Object), SPVersion As String, Optional creds As NetworkCredential = Nothing, Optional KeepCheckedOut As Boolean = False, Optional Comment As String = "") As String Dim result As String = String.Empty If Not (String.IsNullOrEmpty(webURL) OrElse String.IsNullOrEmpty(libraryName) OrElse String.IsNullOrEmpty(FileName)) Then If properties Is Nothing Then properties = New Dictionary(Of String, Object) If Not (properties.ContainsKey("Title") OrElse properties.ContainsKey("vti_title")) Then properties.Add("Title", Path.GetFileNameWithoutExtension(FileName)) End If Dim parameters As New List(Of RPCParameter) parameters.Add(New RPCParameter("document", documentDetailString(libraryName & "/" & FolderPath & "/" & FileName, properties), False, False)) parameters.Add(New RPCParameter("put_option", "overwrite,createdir,migrationsemantics", False, False)) 'Overwrites files (otherwise use edit),creates the directory as needed, preserves creation/modification information parameters.Add(New RPCParameter("comment", Comment)) parameters.Add(New RPCParameter("keep_checked_out", IIf(KeepCheckedOut, "true", "false"))) Dim data As New List(Of Byte) 'put document method: http://msdn.microsoft.com/en-us/library/ms479623(v=office.14).aspx data.AddRange(CommandBytes("put document", SPVersion, parameters)) data.AddRange(Encoding.UTF8.GetBytes(vbLf)) data.AddRange(fileBytes) result = ExecuteRPC(webURL, data.ToArray, creds) End If Return result End Function End Module
The UploadToSharePoint function takes a web URL (this does not have to be the root site in a site collection), the name of the library to upload into, the folderpath (only the immediate directory will be created if needed, if you need to create a full path of directories or ensure they are there, use the CreateFolder function demonstrated in the previous post), the filename (great time to use that UniqueFileName function!), the filebytes, a property dictionary (string, object), the SharePoint version, and some optional parameters (credentials, keepcheckedout, version comments).
If the property dictionary doesn’t contain a Title property, we automatically add one using the filename without the extension (lines 136-138). Now it’s time to create the method parameters.
The first parameter for the put document method is the document parameter (line 141) which we create using the documentDetailString function. Notice that we don’t just specify the filename, but rather the exact path (library + folderpath + filename). We also disable string encoding in the RPCParameter object since this parameter requires very specific encoding that is handled in our helper function.
The second parameter, put_option, is where we specify some combination of the following three strings separated by commas:
- overwrite: overwrites existing files with the same filename
- createdir: creates the parent directory (does not create a full path)
- migrationsemantics: preserves the author, editor and creation information (requires administrative permissions or it is ignored)
For our purposes we are specifying all three (line 142).
The third parameter, comment, is a comment to include for the given version of the file (line 143).
The final parameter, keep_checked_out, takes a string of “true” or “false” (line 144). When true, the document is checked in then checked right back out. When false, the document is just checked in.
To upload the document we must specify all of the method parameters first. We generate the appropriate bytes using our CommandBytes function (line 148). We then have to add the actual file bytes (line 150) but these need to be separated from the command bytes with a line feed (line 149).
That’s it! Now you can upload documents while setting the metadata all in a single call!