More Soma Please…

Developers are in a race with the Universe to create better idiot-proof programs, while the Universe is trying to create better idiots. So far the Universe is winning.

Sharepoint (MOSS): Creating Document Library Items / Uploading Files to a Document Library

Posted by Phil Harding on January 14, 2009

Creating Sharepoint (MOSS) List items is pretty straight forward using Sharepoints Webservices, it’s also straight forward to create Sharepoint Document Library items using Sharepoints Webservices, and as a bonus you don’t have use any CAML.

Using the CopyIntoItems method of the Sharepoint Copy Webservice you can upload a new file into a document library, and/or create a new document library item based on a copy from some other Sharepoint source, i.e. a  linked copy (more on this at the end of the post). When creating the document library item this method also allows you to set the items column meta data, in one operation, anyway, enough talk more code….

private void UploadToSharepointDocumentLibrary( string documentLibraryName,
                CopySoapClient spCopySvc,
                string cDescription,
                string libraryItemUrl,
                string libraryItemFileXml)
{
 string[] targetUrls = new[] { libraryItemUrl };
 byte[] libraryItemBytes = Encoding.ASCII.GetBytes(libraryItemFileXml);

 // library item meta data (columns)
 List<FieldInformation> metaData = new List<FieldInformation>();
 var fi = new FieldInformation();
 fi.DisplayName = "Description";
 fi.InternalName = "Description0";
 fi.Type = FieldType.Note;
 fi.Value = cDescription;
 metaData.Add(fi);

 // call SP wbservice
 CopyResult[] cResults;
 spCopySvc.CopyIntoItems("+", targetUrls, metaData.ToArray(), libraryItemBytes, out cResults);
 if (cResults.Length < 1)
    writeLineTrace("Unable to create a document library item");
 else if (cResults[0].ErrorCode != CopyErrorCode.Success)
 {
    writeLineTrace(
       string.Format(
          "Unable to create a document library item,{0}- ErrorCode = {1}{0}- ErrorMessage = {2}{0}- Destination Url = {3}",
            Environment.NewLine, cResults[0].ErrorCode, cResults[0].ErrorMessage, cResults[0].DestinationUrl));
 }
 else
 {
    writeLineTrace(string.Format("Created a document library item"));
    UnlinkDocumentLibraryItem(documentLibraryName, libraryItemUrl);
 }
}

Note:

documentLibraryName is the unencoded name of (only) the document library including space characters.

libraryItemUrl is the full URL of the document library item, and this URL should be URL encoded (spaces replaced with %20 etc) 

The use of the FieldInformation class to assign meta data to the document library item

Notice the 1st parameter to the CopyIntoItems method where I pass a “+” character. This parameter should contain the source url of the item being copied (and uploaded), however very handily, you can pass anything in this parameter except the empty string, and therefore using this technique you can introduce/upload new document library items and not just new items based on copies of items from elsewhere. The CopyIntoItems method does not validate the source url passed, so it’s safe to put anything in there, except the empty string, in which case the method call fails.

Linked Copies.

Using the technique described above creates a new document library item which is based on a copy of the source item (1st parameter), what Sharepoint calls a Linked Copy. If you look at the properties for a document library item which is a linked copy you’ll see something like;

Linked Copy Properties

If you pass a valid URL as the 1st parameter to CopyIntoItems, then this is what you’ll see when you view the new items properties. This may be a problem, or not, and as you can see you can unlink the new item (the copy!) from it’s source. If you pass a space character, or the ”+” sign as I’ve done, as the 1st parameter, when you view the new items properties you won’t see this message at all, Sharepoint “mostly” treats it as if it weren’t a copy. I say mostly, because if you expand the items control menu, you’ll see the “Go to Source Item” menu command displayed, clicking on this however does nothing.

Linked Copy Control Menu

So, how do you completely “Unlink” a copy (even if it’s a fake copy!) from it’s source. The answer for me turns out not to work, however, other people seem to have had it working though so I’ll present my code here, which also demonstrates nicely how you can use LINQ to XML classes with Sharepoint. The UnlinkDocumentLibraryItem(…) method attempts to set the document library items internal “_CopySource” field to an empty string in order to get rid of the command menus “Go to Source Item” command, for me this doesn’t work, although I get no error message, though other people have reported this to work.

private void UnlinkDocumentLibraryItem(string documentLibraryName, string libraryItemUrl)
{
 string listsSvcUrl = ConfigurationManager.AppSettings["mboscatlistsurl"];
 if (string.IsNullOrEmpty(listsSvcUrl) || string.IsNullOrEmpty(documentLibraryName) ||
         string.IsNullOrEmpty(libraryItemUrl))
  return;
 // create a service binding with security
 var svcBinding = CreateBinding<BasicHttpBinding>(new TimeSpan(0, 10, 0));
 CreateSecurityBinding(svcBinding);
 // create the svc
 EndpointAddress listsSvcEndpoint = new EndpointAddress(listsSvcUrl);
 var spListsSvc = new ListsSoapClient(svcBinding, listsSvcEndpoint);
 spListsSvc.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
 // create the CAML query
 var caml = new XElement("Batch"
         , new XAttribute("OnError", "Continue")
         , new XAttribute("PreCalc", "TRUE")
         , new XElement("Method"
              , new XAttribute("ID", "1")
              , new XAttribute("Cmd", "Update")
              , new XElement("Field"
                   , new XAttribute("Name", "ID")
               )
              , new XElement("Field"
                   , new XAttribute("Name", "FileRef")
                   , new XText(libraryItemUrl.Replace("%20", " "))
               )
              , new XElement("Field"
                   , new XAttribute("Name", "_CopySource")
                   , new XText("")
               )
          )
  );
 XmlNode ndResults = spListsSvc.UpdateListItems(documentLibraryName, caml.GetXmlElement());
 var listItemResult = CheckListUpdateResults(ndResults);
 if (listItemResult.Key < 1)
    writeLineTrace(string.Format("Unable to Unlink system exception report @ Mbos: {0}", listItemResult.Value));
 else
    writeLineTrace(string.Format("Unlinked system exception report @ Mbos"));
}

Note:
documentLibraryName is the unencoded name of (only) the document library including space characters.

libraryItemUrl is the full URL of the document library item, and this URL should not be URL encoded (%20 replaced with space etc).

The use of the FileRef field in the CAML query which is used to locate the document library item to update, in contrast to updating List items which requires the ID field be used to locate the list item to update.

Check here for the CheckListUpdateResults(…) method.

In order to use LINQ to XML classes with Sharepoint Webservices, you’ll need to convert an XElement to say an XmlNode and vice versa, and C# 3.0 extension methods are perfect for this

public static class MyExtensions
{
 public static XElement GetXElement(this XmlNode node)
 {
     XDocument xDoc = new XDocument();
     using (XmlWriter xmlWriter = xDoc.CreateWriter())
     {
        node.WriteTo(xmlWriter);
     }
     return xDoc.Root;
 }

 public static XmlNode GetXmlNode(this XElement element)
 {
     var xnNode = (XmlNode)GetXmlDocument(element);
     return xnNode;
 }

 public static XmlDocument GetXmlDocument(this XElement element)
 {
     using (XmlReader xmlReader = element.CreateReader())
     {
         XmlDocument xmlDoc = new XmlDocument();
         xmlDoc.Load(xmlReader);
         return xmlDoc;
     }
 }

 public static XmlElement GetXmlElement(this XElement element)
 {
     var xeNode = GetXmlDocument(element).DocumentElement;
     return xeNode;
 }
}

.

4 Responses to “Sharepoint (MOSS): Creating Document Library Items / Uploading Files to a Document Library”

  1. derek said

    CopyIntoItems will overwrite the target file if the target file exists. Is there a simple method to check whether the target file already exists?

    Thanks

  2. Hi Derek, an easy way you say :-)

    Not that I know of, but you can use the SPQuery object or use the Lists Webservice with a CAML query.

  3. Eve said

    Hi Phil,
    Have you heard of any issues with using copyintoitems for libraries with versioning enabled? I’m trying to “copy” a file from my local machine. The CopyResult error message keeps telling me that the underlying collection has been modified while enumerating – and none of my metadata gets applied. It works fine on doc libs without versioning.

    Scratching head…

  4. Hi Eve, I’ve not come across this before, have you tried his sequence: Checkout, Upload, Checkin?

    The Lists webservice has methods to do the CheckIn/Out

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>