Many developers new to SharePoint want to know how to get content into
SharePoint from another application. This application is usually a remote
application either running in a asp.net application or a desktop application.
The developers may be familiar with the SharePoint object model and how to use
it to put content into SharePoint. However, when it comes to doing this remotely
there seems to be a lot of confusion. This confusion is brought on by the fact
that there are numerous out of the box ways of doing this. You can put content
into SharePoint by using web services, WebDav or frontpage remote procedure
calls. The problem arises when developers chose a method and find out that
certain methods don’t support certain functions that you would normally see
either from the SharePoint UI or from the SharePoint object model. This article
will give you a brief description of the methods available for remotely putting
content into SharePoint and compare the methods based on certain factors you
should be aware of as a SharePoint developer. These factors include complexity,
folder creation, scalability and indexing. Complexity and scalability are rated
on a scale of 1 through 10.
Method | Complexity | Scalability | Indexing | Folder Creation |
Copy.asmx | 5 | 4 | yes | yes* |
WebDav | 2 | 5 | yes* | yes* |
Rpc | 10 | 10 | yes | yes |
* must be used in conjunction with Lists.asmx web service
Copy Web Service
To create content remotely the copy web service is probably your best bet.
The copy web service enables you to create new documents and send the metadata
for indexing in one call. This makes the web service more scalable. Many times
users want to create a new folder to store their documents. Unfortunately, the
copy web service does not have a method for creating a folder. The following is
a code snippet for creating new content in SharePoint via the copy webs
service:
public static void CreateNewDocumentWithCopyService(string fileName)
{
copyservice.Copy c = new copyservice.Copy();
c.Url = "http://servername/sitename/_vti_bin/copy.asmx";
c.UseDefaultCredentials = true;
byte[] myBinary = File.ReadAllBytes(fileName);
string destination = http://servername/sitename/doclibrary/
+ Path.GetFileName(fileName);
string[] destinationUrl = {
destination };
copyservice.FieldInformation info1 = new
copyservice.FieldInformation();
info1.DisplayName = "Title";
info1.InternalName = "Title";
info1.Type =
copyservice.FieldType.Text;
info1.Value = "new title";
copyservice.FieldInformation info2 = new
copyservice.FieldInformation();
info2.DisplayName = "Modified
By";
info2.InternalName = "Editor";
info2.Type =
copyservice.FieldType.User;
info2.Value =
"-1;#servername\\testmoss";
copyservice.FieldInformation[] info = { info1, info2 };
copyservice.CopyResult resultTest = new
copyservice.CopyResult();
copyservice.CopyResult[] result = {
resultTest };
try
{
//When creating new
content use the same URL in the SourceURI as in the Destination URL argument
c.CopyIntoItems(destination, destinationUrl, info, myBinary,
out result);
}
catch (Exception ex)
{
}
}
The benefits of using the copy web service is that it is simple to code
against. Most developers are familiar with the web service programming. Also the
copy web service supports creating content and metadata in one call thus not
creating multiple versions. Unfortunately, this method suffers from one problem
and that is the use of byte arrays. If you plan on uploading large files lets
say in excess of 2mb, then chances are you will receive sporadic “out of memory”
errors. It may not happen on your development server but may happen on your
production server. This is because the windows OS needs to allocate byte arrays
with contiguous memory. If the server’s memory is fragmented (has a lot of
available memory but not much contiguous memory) then you will receive this
error. Thus, the copy web service is not very scalable. Finally, web services
tend to be verbose given their soap protocol and the marshalling from string to
native types makes them slower than other methods.
WebDav
Most developers are familiar with WebDav because it is used to display
document libraries in Windows Explorer. Here the familiar dragging and dropping
of files into SharePoint can be accomplished. You can accomplish the same thing
by using the System.Net.WebClient class as follows:
public static void UploadFile(string fileName, string destination)
{
WebClient wc = new WebClient();
wc.UseDefaultCredentials =
true;
byte[] response = wc.UploadFile(destination +
Path.GetFileName(fileName) , "PUT", fileName);
}
Ok this seems simple enough. As you can see it is not as complex as using the
Copy web service. However, it does not support sending any metadata long with
the file content. This can be a major problem if the document library has
multiple content types, so the new file will be put into the document library
with the default content type. Another big issue is if the default content type
has required fields. The file will remain checked out until the fields are
populated. This prevents other users from seeing the document or from being
returned in any searches. It is a great solution if you are just bulk migrating
data from an external data store to SharePoint. You more than likely will have
to do extra work afterwards. Adding metadata after uploading will also cause the
creation of extra versions of the document being created unnecessarily. The fact
that it does not use the soap protocol but straight http makes it more scalable
than the copy web service. Unfortunately, it still suffers from the fact that it
uses a byte array to upload the file. So sooner or later you will run into “out
of memory “ exceptions. So how can I create a folder before using WebDav? You
can use the lists web service to accomplish this:
public static XmlNode UpdateListItemCreateFolder(string docLibraryName,
string folderName)
{
listservice.Lists listProxy = new
listservice.Lists();
string xmlFolder = "<Batch OnError='Continue'><Method ID='1'
Cmd='New'><Field Name='ID'>New</Field><Field
Name='FSObjType'>1</Field><Field Name='BaseName'>” + folderName +
“</Field></Method></Batch>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFolder);
XmlNode batchNode = doc.SelectSingleNode("//Batch");
listProxy.Url = "http://servername/sitename/_vti_bin/lists.asmx";
listProxy.UseDefaultCredentials = true;
XmlNode resultNode = listProxy.UpdateListItems(docLibraryName,
batchNode);
return resultNode;
}
FrontPage RPC (Remote Procedure
Calls)
Most developers are not familiar with RPC and what it can do.
The complexity of coding RPC is high due to the fact that construction of
commands and the interpreting of responses can be tedious and error prone.
However this method proves to be the most scalable and the fastest. It also
supports sending both the content and the metadata in one call. RPC has numerous
commands including one for creating folders and it supports the use of streams
rather than a byte array. Below is sample code to create a new document in
SharePoint using RPC with a stream.
public static void CreateDocumentRPC(string name, string docLib, string
title, bool overWrite, Stream fileBinary)
{
string method = "put document: 12.0.0.4518";
string serviceName = "http://servername/sitename/_vti_bin/_vti_aut/author.dll";
string document = docLib + "/" + name;
string
metaInfo = string.Empty;
string putOption = overWrite ?
"overwrite" : "edit";
string keepCheckedOutOption = "false";
string comment = string.Empty;
string returnStr
= string.Empty;
byte[] data;
string returnError
= string.Empty;
string fpRPCCallStr =
"method={0}&service_name={1}&document=[document_name={2};meta_info=[{3}]]&put_option={4}&comment={5}&keep_checked_out={6}";
method = HttpUtility.UrlEncode(method);
putOption
= HttpUtility.UrlEncode(putOption);
metaInfo = "vti_title;SW|" +
title;
fpRPCCallStr = String.Format(fpRPCCallStr, method,
serviceName, document, metaInfo, putOption, comment, keepCheckedOutOption);
try
{
//add line feed
character to delimit end of command
byte[] fpRPCCall =
System.Text.Encoding.UTF8.GetBytes(fpRPCCallStr + "\n");
data = new byte[fpRPCCall.Length];
fpRPCCall.CopyTo(data, 0);
HttpWebRequest wReq = WebRequest.Create(serviceName) as
HttpWebRequest;
wReq.Credentials =
System.Net.CredentialCache.DefaultCredentials;
wReq.Method =
"POST";
wReq.ContentType =
"application/x-vermeer-urlencoded";
wReq.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
wReq.ContentLength = fpRPCCall.Length + fileBinary.Length;
using (Stream requestStream = wReq.GetRequestStream())
{
requestStream.Write(fpRPCCall, 0, fpRPCCall.Length);
byte[] tmpData = null;
int
bytesRead = 0;
int chunkSize = 2097152;
int tailSize;
int chunkNum =
Math.DivRem((int)fileBinary.Length, chunkSize, out tailSize);
//chunk the binary directly from the stream to buffer.
for (int i = 0; i < chunkNum; i++)
{
data = new
byte[chunkSize];
bytesRead =
fileBinary.Read(tmpData, 0, chunkSize);
requestStream.Write(data, 0, chunkSize);
}
//send the remainde if any.
if
(tailSize > 0)
{
data =
new byte[tailSize];
bytesRead =
fileBinary.Read(data, 0, tailSize);
requestStream.Write(data, 0, tailSize);
}
//Now get the response from the server
WebResponse response = wReq.GetResponse();
int num2,num3;
long
contentLength = response.ContentLength;
bool noLength =
false;
if (contentLength == -1)
{
noLength = true;
contentLength = chunkSize;
}
byte[] returnBuffer = new byte[(int) contentLength];
using (Stream responseStream =
response.GetResponseStream())
{
num3 = 0;
do
{
num2 = responseStream.Read(returnBuffer, num3,
((int) contentLength) - num3);
num3 += num2;
if (noLength && (num3 == contentLength))
{
contentLength += chunkSize;
byte[] buffer2 =
new byte[(int) contentLength];
Buffer.BlockCopy(returnBuffer, 0, buffer2, 0, num3);
returnBuffer = buffer2;
}
}
while (num2 != 0);
}
if (noLength)
{
byte[] buffer3 = new byte[num3];
Buffer.BlockCopy(returnBuffer, 0, buffer3, 0, num3);
returnBuffer = buffer3;
}
returnStr = Encoding.UTF8.GetString(returnBuffer);
}
}
catch (Exception ex)
{
//error handling
}
}
As you can see the complexity of coding against rpc can be daunting. You can
refactor this code into something much more reusable. Parsing of the return
response can be a bit strange also. Below is an example of a successful document
creation response from the SharePoint server:
<html><head><title>vermeer RPC
packet</title></head>
<body>
<p>method=put
document:12.0.0.4518
<p>message=successfully put document
'tester2/crpc.png' as 'tester2/crpc.png'
<p>document=
<ul>
<li>document_name=tester2/crpc.png
<li>meta_info=
<ul>
<li>vti_rtag
<li>SW|rt:61935CFA-736B-4311-97AA-E745777CC94A@00000000001
<li>vti_etag
<li>SW|"{61935CFA-736B-4311-97AA-E745777CC94A},1"
<li>vti_filesize
<li>IR|1295
<li>vti_parserversion
<li>SR|12.0.0.6318
<li>vti_modifiedby
<li>SR|BASESMCDEV2\test.moss
<li>vti_timecreated
<li>TR|19 May 2009 17:28:35 -0000
<li>vti_title
<li>SW|wackout
<li>vti_lastheight
<li>IX|78
<li>ContentTypeId
<li>SW|0x010100B1C4E676904AB94BA76515774B23E02D
<li>vti_timelastmodified
<li>TR|19 May 2009 17:28:35 -0000
<li>vti_lastwidth
<li>IX|411
<li>vti_author
<li>SR|BASESMCDEV2\test.moss
<li>vti_sourcecontrolversion
<li>SR|V1.0
<li>vti_sourcecontrolcookie
<li>SR|fp_internal
</ul>
</ul>
</body>
</html>
If there is not substring of “osstatus” or “Windows SharePoint Services
Error” then it is a success.
I hope this helps in your evaluation of different ways you can upload data to
SharePoint. There are many things to consider when selecting which method to
use. High on your priority list should be speed, scalability and the ability to
send metadata. Sometimes the method that looks the hardest can be the best
choice and it is up to you as a developer to abstract away the complexity.