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

July 3, 2008

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";

            string[] url;
            if (string.IsNullOrEmpty(destinationFolderPath))
                url = new string[] { string.Format("{0}/{1}/{2}", _siteUrl, _name, fileName) };
                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