Upload a file to Sharepoint 2007 using webservices with a specific content type


I’m moving to a new site ! Please visit this article to by clicking the following url: Upload a file to Sharepoint 2007 using webservices with a specific content type

These days I’ve been involved in a migration project from Sharepoint 2001 to MOSS 2007, what  I had to do was move all the documents from a document workspace in SPS2001 to a WSS3.0 document library. Since some of the custom metada had to be migrated with the documents and the SP2001 profiles had to be converted to MOSS content types I decided that the best approach would be to use MOSS 2007 web services interface, most specifically the Copy.asmx web service which has a CopyIntoItems method that enables to upload files to a document library.

Since SP2001 is an obsolete platform, I won’t talk here about how to access it and deal with the PKMCDO COM library (if there is enough interest, I might write an article about that) and I will focus on the file upload web service and the more challenging topic (imho) which is how to set a content type to the uploaded documents.

The first thing to do would be to get the files uploaded to MOSS. As I told, I will be using the Copy.asmx web service. Following is the code of the upload function:

        public void UploadFile(string destinationFolderPath,
                               byte[] fileBytes,
                               string fileName,
                               bool overwrite,
                               string sourceFileUrl,
                               string lastVersionUrl)
        {

            List<Sharepoint.FieldInformation> fields = new List<Sharepoint.FieldInformation>();
            Sharepoint.FieldInformation fieldInfo;

            fieldInfo = new Sharepoint.FieldInformation();
            fieldInfo.Id = Microsoft.SharePoint.SPBuiltInFieldId.Title;
            fieldInfo.Value = "New title";
            fieldInfo.DisplayName = "Title";
            fieldInfo.Type = YetAnotherMigrationTool.Library.SP2007.Sharepoint.FieldType.Text;
            fieldInfo.InternalName = "Title";
            fields.Add(fieldInfo);

            string[] url;
            if (string.IsNullOrEmpty(destinationFolderPath))
                url = new string[] { string.Format("{0}/{1}/{2}", _siteUrl, _name, fileName) };
            else
                url = new string[] { string.Format("{0}/{1}/{2}{3}", _siteUrl, _name, destinationFolderPath, fileName) };
            Sharepoint.CopyResult[] result;

            Sharepoint.Copy service = new Sharepoint.Copy();
            service.Url = _siteUrl + "/_vti_bin/Copy.asmx";
            service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);
            service.Timeout = 600000;

            uint documentId = service.CopyIntoItems(sourceFileUrl, url, fields.ToArray(), fileBytes, out result);
        }

There is nothing really difficult there. It necessary to call the webservice with an account that has Manage List permissions on the target document library. Also we should estimate the maximum document size we plan to upload and network speed and set the maxRequestLength and executionTimeout values of the httpRuntime section in our web.config in accord to avoit any possible Timeout Exception.

I’ve included an example of how would we set any of the document’s properties when calling the CopyIntoItems method. I’ve used the Title property to show that even read only fields can be assigned with that method. So, can all the fields be assigned ? Unfortunately not.

One of my requirements was to map SP2001 document profiles to MOSS content types, in order to do that, I created the content types at the server with their custom columns and looked for a way to pass this info as a parameter to the CopyIntoItems service call. There was not a parameter such like that, but I was confident that I would be able to set the ContentType field just as I did with the Title field and that would be all. However, that solution didn’t work. No matter if you set the ContentType or the ContentTypeId fields before calling the Copy.asmx service that the uploaded document content type will always be the default content type of the library.

After doing that I tried another approach. As I saw that the default content type of the library was assigned to the uploaded files, I tried to change the default content type of the list before uploading each file. Unfortunately, I couldn’t find any webservice that provided that functionality, I tried the UpdateList method of the Lists.asmx service with a custom xml scheme where the <DEFAULT></DEFAULT> section of the content type element was replaced by the one I wanted to be with no luck. I couldn’t manage to get the default content type of a document library changed !

Finally, I tried the last solution I had thought of. It was to use the UpdateListItems method of the Lists.asmx service to change the item ContentType field. The reason why I didn’t try this approach first instead of trying to change the default content type of the document library (which would seem the obvious thing to do) was because I didn’t expect it to work. If I hadn’t been able to set that readonly field with the CopyIntoItems method of the Copy.asmx service it would be expected for the UpdateListItems to have the same behaviour. But it doesn’t. It is possible to update any field with this method and eventually change a document content type.

So here is the method I use to call that service and change the content type:


        public void SetContentType(List<string> ids, string contentType)
        {
            ListsService.Lists service = new YetAnotherMigrationTool.Library.SP2007.ListsService.Lists();
            service.Url = _siteUrl + "/_vti_bin/Lists.asmx";
            service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);

            string strBatch = "";
            for (int i = 1; i <= ids.Count; i++)
            {
                strBatch += @"<Method ID='"+i.ToString()+@"' Cmd='Update'><Field Name='ID'>" + ids[i-1] + "</Field><Field Name='ContentType'>"+contentType+"</Field></Method>";
            }
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement elBatch = xmlDoc.CreateElement("Batch");
            elBatch.SetAttribute("OnError", "Continue");
            elBatch.SetAttribute("ListVersion", "10");
            elBatch.SetAttribute("ViewName", "");
            elBatch.InnerXml = strBatch;

            result = service.UpdateListItems(_name, elBatch);
        }

I called this method passing as parameters the collection of ids I got from the CopyIntoItems call, grouping by content type and it worked ! Now I can set the content type to my uploaded files and my first big issue with that migration project is solved. Currently I am working on a more complicated step which is how to migrate the version history from the SP2001 documents to the MOSS 2007, if I finally manage to solve it I’ll surely post a new article on that topic. That’s all for now, as always, comments are welcome !

Doctora Rosa Grau, Pediatra, Sant Quirze del Valles

18 Responses to Upload a file to Sharepoint 2007 using webservices with a specific content type

  1. Michael says:

    Recently I had a simular challange, converting a SPS 2001 to MOSS 2007. I thought I had the last 2001. I guess not 🙂 Any way I like your solution much better than what I went through to accomplish this task. I wished I had thought of using the web services! Thanks for a Great post.

  2. Jason says:

    Nice work. I like using the FieldInformation object to upload the document and populate metadata in one operation. When using the CopyIntoItems method, the copied document is linked to the original. That makes sense, based on the intended use of the object.

    I am using the CopyIntoItems method to copy items from a file share to the SharePoint. In this case not only is the link unwanted, but is also broken.

    Have you found a way to have the Copied files be Unlinked?

  3. oricode says:

    I´ve tried it but didn´t manage to do it. I’ve tried to update to link field using a FieldInformation object without any success. The field can´t be updated.

  4. Jason says:

    I think I found a hack that will work for uploading an “unlinked” document using this method.

    In this line:
    uint documentId = service.CopyIntoItems(sourceFileUrl, url, fields.ToArray(), fileBytes, out result);

    if you change the sourceFileUrl to a single space ” “, the operation executes and the resulting file in the SharePoint shows no file link. I’m working with VB code, does this work in c# as well?

    I don’t know if there are any unseen consequences of this action, but at first glance it appears to work.

  5. oricode says:

    Wow ! Nice found if it works !

    I actually tried to pass an empty string as a parameter but then the CopyIntoItems method raised an exception

  6. Jason says:

    Ok, one more. Passing the single space almost works. It does remove the messages at the top of the properties page, but internally the SharePoint still thinks its linked. If you look a the dorpdown on a document uploaded using this method, there is still an option to “Go to Source Item.” Which doesn’t work. On the bright side it doesn’t produce an error either

    If you modify line 10 of the SetContentType routine and add:

    ” + ”

    That appears to remove all traces of the “Link to source” Again, I’m working in VB, but would be interested to know if these things work in C# as well.

  7. Jason says:

    The last comment dodn’t show the code line:

    Add another element Field Name=’_CopySource’ and then close the field

    CODE: “”+””

  8. oricode says:

    Really good contribution Jason, Thanks a lot !

    I confirm it works as well in c# code, I’ll update the article with that info

  9. Ted says:

    You said that you passed the collection of IDs that you got from the CopyIntoItems call into the SetContentType call. I don’t see any IDs in the CopyIntoItems result. Can you show how the output of your one method fed the other? Thanks!

  10. […] Change an item content type with a SQL Query Some months ago, I was involved in a document library migration from SP2001 to SP2007 project. One of the big problems I found was how to upload all the files to Sharepoint with the standard web services interface and set the content type of the items. You can read more about that issue and the solution implemented here […]

  11. Maroc says:

    Hey.
    I’ve spent so many hours on just trying to get the items content type to insert, but no matter what I try it always reverts to the default content type.
    I even tried your claim of updating the content type after the item’s on the site, but it still failed.
    Can someone please help me?

    Many thanks in advance.

    • oricode says:

      I must admit that there are a lot of problems when trying to set the content type. The method I suggested worked for me but I must admit that during a large migration project (around 50Gb of files) occasionally I would find some content types not properly set (about maybe a 10% of the overall number of documents)

      What I ended up doing was to organize the items in folders with one content type associated with each folder. Then change the default content type of each folder to the appropiate one and with this approach, all the content types were properly set.

      Hope that helps.

  12. Maroc says:

    What I’m doing is much simpler than that.
    it’s just one item that gets created on a calendar list at a time. That’s all. So bothersome.

  13. oricode says:

    Then why don’t you set the default content type of the list to the content type you are trying to assign ?

  14. Maroc says:

    Because the list has 2 content types for 2 types of jobs I guess you could put it. There’s no real default between the two types so that can’t be done unfortunately

  15. Maroc says:

    void SetContentType(String contentTypeSearch, String itemID)
    {
    StringBuilder tmpInnerXml = new StringBuilder();
    XmlDocument doc = new XmlDocument();
    XmlElement updates = doc.CreateElement(“Batch”);
    XmlNode node = null;
    String contentID = String.Empty;
    String contentName = String.Empty;

    try
    {
    try
    {
    foreach (XmlNode currContType in xnlContentTypes)
    {
    if (currContType.Attributes[“Name”].Value.Contains(contentTypeSearch))
    {
    contentID = currContType.Attributes[“ID”].Value;
    contentName = currContType.Attributes[“Name”].Value;
    break;
    }
    }
    }
    catch (Exception ge)
    {
    throw new Exception(“Failed to specify Content Type.”, ge.InnerException);
    }

    try
    {
    tmpInnerXml.Append(“”);
    tmpInnerXml.Append(“” + itemID + “”);
    tmpInnerXml.Append(“” + contentID + “”);
    tmpInnerXml.Append(“The update completed”);
    tmpInnerXml.Append(“”);
    updates.InnerXml = tmpInnerXml.ToString();
    }
    catch (Exception ex)
    {
    throw new Exception(“Failed to buildup xml query: ” + ex.Message, ex.InnerException);
    }

    try
    {
    node = spListWebService.UpdateListItems(GetListGUID(), updates);

    Int32 start = -1;
    start = node.OuterXml.ToString().IndexOf(“ows_ID=\””) + 8;
    Int32 end = node.OuterXml.ToString().IndexOf(‘”‘, start);
    Int32 l = end – start;

    if (start – 8 != -1)
    {
    String temp = node.OuterXml.ToString().Substring(start, l);
    }
    else
    {
    throw new Exception(node.OuterXml.ToString());
    }
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message, ex.InnerException);
    }
    }
    catch (Exception ex)
    {
    throw new Exception(“SetItemStatus Error: ” + ex.Message, ex.InnerException);
    }
    }

    That’s my method for setting the ContentType. It runs with no errors and the Title updates. The only issue is the ContentType not updating.

  16. BR says:

    This approach worked fine for new content, but (for some reason) updating existing content restored content type to library default.

    Luckily, There is another way to make CopyIntoItems respect content type. Use:

    I came to this by testing GetItem response.

    BTW, I have been working with SP web services for almost 2 months and it is soo messy, I can only conclude that this API was written by a bunch of interns at Microsoft.

  17. BR says:

    Oops, this form did not like tags. Here it is one more try:

    <FieldInformation Type=”Choice” DisplayName=”Content Type” InternalName=”ContentType” Value=”Voucher” />

Leave a reply to oricode Cancel reply