Update: You can download a comprehensive c# class library to automate RPC calls - including uploading files to a SharePoint document library. See this blog post for more information.
using System;using System.Collections.Generic;using System.Text;using System.IO;using System.Net;using System.Web;namespace DevHoleDemo{class Program
{static void Main(string[] args)
{Dictionary<string, object> properties = new Dictionary<string, object>();
properties.Add("Title", "Test Title");
//Create or overwrite text file test.txt in 'Docs' document library creating folder 'Test Folder' as required.DocLibHelper.Upload("http://localhost/Docs/Test Folder/test.txt", System.Text.Encoding.ASCII.GetBytes("Test text."), properties);
}
}
static class DocLibHelper
{static string EncodeMetaInfo(Dictionary<string, object> metaInfo)
{if (metaInfo == null) return "";
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, object> kvp in metaInfo)
{if (kvp.Value != null)
{string fieldName = kvp.Key; // note: field names are case sensitive
switch (fieldName) {case "Title":
fieldName = "vti_title"; break;}
string data = EscapeVectorChars(kvp.Value.ToString());string dataTypeCode = "S";
switch (kvp.Value.GetType().FullName) {case "System.Boolean":
dataTypeCode = "B"; break;case "System.DateTime":
data = ((DateTime)kvp.Value).ToString("s") + "Z";
break;}
sb.AppendFormat("{0};{1}W|{2};", fieldName, dataTypeCode, data);}
}
return HttpUtility.UrlEncode(sb.ToString().TrimEnd(';'));
}
static string EscapeVectorChars(string value)
{StringBuilder sb = new StringBuilder();
foreach (char c in value)
{ switch (c) {case ';':
case '|':
case '[':
case ']':
case '\\':
sb.Append("\\"); break;}
sb.Append(c);
}
return sb.ToString();}
public static string GetWebURL(string url)
{ try { url = url.Substring(0, url.LastIndexOf("/")); try {using (WebClient webClient = new WebClient())
{ webClient.Credentials = CredentialCache.DefaultCredentials;webClient.Headers.Add("Content-Type", "application/x-vermeer-urlencoded");
webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
byte[] data = Encoding.UTF8.GetBytes("method=open+service%3a12.0.4518.1016&service_name=%2f");
string result = Encoding.UTF8.GetString(webClient.UploadData(url + "/_vti_bin/_vti_aut/author.dll", "POST", data));
if (result.IndexOf("\n<li>status=327684") == -1)
return url;throw new Exception();
}
}
catch { return GetWebURL(url);}
}
catch {return null;
}
}
public static bool Upload(string destinationUrl, byte[] bytes, Dictionary<string, object> metaInfo)
{string result = "";
string webUrl = GetWebURL(destinationUrl); string documentName = destinationUrl.Substring(webUrl.Length + 1);return Upload(webUrl, documentName, bytes, metaInfo, out result);
}
public static bool Upload(string webUrl, string documentName, byte[] bytes, Dictionary<string, object> metaInfo, out string result)
{string putOption = "overwrite,createdir,migrationsemantics"; // see http://msdn2.microsoft.com/en-us/library/ms455325.aspx
string comment = null;
bool keepCheckedOut = false;
string method = "method=put+document%3a12.0.4518.1016&service_name=%2f&document=[document_name={0};meta_info=[{1}]]&put_option={2}&comment={3}&keep_checked_out={4}\n";
method = String.Format(method, documentName, EncodeMetaInfo(metaInfo), putOption, HttpUtility.UrlEncode(comment), keepCheckedOut.ToString().ToLower());
List<byte> data = new List<byte>();
data.AddRange(Encoding.UTF8.GetBytes(method));data.AddRange(bytes);
try {using (WebClient webClient = new WebClient())
{ webClient.Credentials = CredentialCache.DefaultCredentials;webClient.Headers.Add("Content-Type", "application/x-vermeer-urlencoded");
webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
result = Encoding.UTF8.GetString(webClient.UploadData(webUrl + "/_vti_bin/_vti_aut/author.dll", "POST", data.ToArray()));
if (result.IndexOf("\n<p>message=successfully") < 0)
throw new Exception(result);
}
}
catch (Exception ex)
{result = ex.Message;
return false;
}
return true;
}
}
}
28 comments:
Hi! Great post!
I have a question for you...
Is it possible to update the metadata called "vti_modifiedby" ?
I have tryed to wrote it as:
vti_modifiedby;SR|MYDOMAIN\user ...
or
vti_modifiedby;SW|MYDOMAIN\user ...
or
vti_modifiedby;SR|MYDOMAIN\\user ...
but nothing appened.
Is only the title that must be referenced as "vti_title"?
Lorenzo
I don't think you can modify the vti_modifiedby meta-key in this way because the access modifer is read-only (see here for information on this particular key, and here for a description of access codes and data types).
One approach to consider in this particular case would be to impersonate the user when you create the new document, e.g.
webClient.Credentials = new NetworkCredential("user", "password", "MYDOMAIN");
Note that with the migrationsemantics put_option parameter set (as it is in the code above), any update to an existing document will preserve the original author and modified by information (as long as the credentials passed are administrator credentials).
There is also a problem with the equal char =
Try to upload a file with a = in a field. It will fail.
The only solutions seems to be to replace the character.
I think that also the # char gets some problems.
Lorenzo
I solved adding the = char in the 'EscapeVectorChars' method.
Lorenzo
This post has helped me tremendously. I have about 700 documents to upload, with metadata on each of them.
I ran into a problem/question, being the SharePoint newbie that I am.
Some of my metadata fields are lookup values into lists, and one is a Person or Group. I blindly just tried putting string text like "mouse" into the lookup metadata field, but it didn't work. I clearly need to put an ID or some reference to the item in there - I just have no idea what to put or how to find out.
Any guidance would be helpful.
I’ve just posted here which explains a bit about how the Person or Group data type is formatted. Basically it’s a lookup value - so to include this as meta data using the code above you’re going to need to know the principal id for each user in question. If you take a look at the Xml property of a document in your document library by using the SharePoint API (e.g. web.Lists["Docs"].Items[0].Xml) you’ll see how the meta data is encoded, which for this data type is as an integer (the principal id), e.g.:
ows_MetaInfo='70;#vti_parserversion:SR|12.0.0.4518
personColumn:IW|1
lookupColumn:IW|72
So to get the code above to work, you’d need to update the EncodeMetaInfo method above to encode integers, e.g.: case "System.Int32": dataTypeCode = "I"; break;
Lookups are similar, in this case the number is the ID of the list item. You can find out this ID without code by changing the view of the list to display the ID.
The link to the post on the formatting of the Person or Group data type is missing above. It should be http://geek.hubkey.com/2008/01/searching-for-users-or-groups-using.html.
Thank you for posting, very slick.
I have a question...
I was able to upload *.txt, and *.doc files. When I try to post an *.mht I receive and error message.
msg=Could not process the file Product Handling Specs/TEST.MHT as a Single File Web Page
Do we need to change any of the settings to save a MHT file?
Thanks
You shouldn't need to change any of the settings to get this to work with valid .mht files. The server does do a check to see that the file content matches the file type in this case, so for example you'll get that sort of message if you attempt to write the simple text string in the code example, i.e.
DocLibHelper.Upload("http://localhost/Docs/Test Folder/test.mht", System.Text.Encoding.ASCII.GetBytes("Test text."), properties);
Instead, when I tested it with a document previously saved as .mht in Word, it worked no problem, e.g.
DocLibHelper.Upload("http://localhost/Docs/Test Folder/test.mht", File.ReadAllBytes(@"D:\Temp\test.mht"), properties);
Thanks txs8311. Looking at your example I was able to fix the mht issue.
Carolina
Would you happen to have some code to delete a specific document from a document library?
Here's an example of using the remove documents RPC method.
Thank you very much for the delete code.
Would you recomend any book to help coding for WSS 2007?
Here's a whole list - not that I've actually read any of them!
what would be error handling strategy for different errors that the RPC would return. Do you have a list of error codes or text that we can check?
To handle specific errors, you'd need to parse the text in the return html string. I haven't seen a list of error codes anywhere - the error message format is described briefly on msdn here. You could extract a list of all error messages from the file \%Program Files%\Common Files\Microsoft Shared\web server extensions\12\BIN\1033\FPEXT.MSG.
Fantastic post! Thanks!
great post...helped me a lot..
Hi,
How to upload mutiple documents in document libarary by using RPC methods.(put document or put documents)please explain with live example.
Thank in advance.
Sunder Chhokar
hi there it work fine but i got a problem, when update the metadata , it should call UpdateListItems() which take a xml to update the columns. i can only get "title" columns to be updated. all other columns cant be updated, even, when called alone. if i update "title" together with another column, both will not be updated.
pls help, i am really new to sharepoint
Martin - you're using the code from Part I of this post, so take a look at InnerXml of the response you're getting from the Lists web service - it may give you some clues. If you're trying to update a date field - you may need to provide it in a special format - e.g. myDate.ToString("s") + "Z"
Other than that, try using the RPC code in this post (Part II) - it uses a different method of updating the meta data - see if it gives you the same problem. If you have access to run code localy on the SharePoint server you could also test updating the fields with some API code which might help you track down the problem.
when i use webClient.Credentials = CredentialCache.DefaultCredentials it works fine in localhost but not on server but when i pass username and password it works fine even on server. any idea?
Thanks, dude, you rock!
Fantastic!! I've been searching for this for days!
Thanks, it is fabulous! do you know how can I add a date to a column which property is set as "Date only" (MM-DD-YY) in sharepoint?
Great post,Thanks alot !
I've encountered a problem escaping field names with special chars (e.g equal sign "=")
Any Ideas ?
Thanks,
Oreng
oreng - anonymous above had the same issue and solved it by adding an equals to the EscapeVectorChars method above. For a more comprehensive treatment of RPC methods than the brief demo code above, you can download a free library of methods from our website. See this post for more details. Thanks.
Excellent Post, but when i use DefaultCredentials for webClient.Credentials on production server, its not working, anonymous access denied...any idea anyone.
Post a Comment