Tuesday, October 23, 2007

Upload a File to a SharePoint Document Library - Part I

The following helper class demonstrates a few techniques that allow documents to be uploaded to a SharePoint document library programmatically without using the API or a custom web service. You don't need to specify a document library name, and it will create any folders specified in the URL as required. File meta data will be updated if any properties are passed.

To use this code add a reference to the SharePoint Lists service (/_vti_bin/Lists.asmx) and name it ‘ListsService’. The code was written against MOSS 2007.

To download files use the GetItem method of the SharePoint Copy service (/_vti_bin/Copy.asmx). While it’s possible to upload files using the CopyIntoItems method of this service, it won’t create folders as needed, and you’d probably want to remove the copy link that is created.

It's also possible to use Front Page Server Extensions and RPC calls to upload files with meta data - the code for which is a bit more efficient as it doesn't require web service calls. Using RPC calls is covered in Part II.

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;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
 
namespace DevHoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            DocLibHelper docLibHelper = new DocLibHelper();
            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);
        }
    }
 
    public class DocLibHelper
    {
        ListsService.Lists m_listService;
        ICredentials m_credentials;
        ListInfoCollection m_lists;
 
        public DocLibHelper()
        {
            m_credentials = CredentialCache.DefaultCredentials;
            m_listService = new ListsService.Lists();
            m_listService.Credentials = m_credentials;
            m_lists = new ListInfoCollection(m_listService);
        }
 
        public class ListInfo
        {
            public string m_rootFolder;
            public string m_listName;
            public string m_version;
            public string m_webUrl;
            public ListInfo(XmlNode listResponse)
            {
                m_rootFolder = listResponse.Attributes["RootFolder"].Value + "/";
                m_listName = listResponse.Attributes["ID"].Value;
                m_version = listResponse.Attributes["Version"].Value;
            }
            public bool IsMatch(string url)
            {
                try
                {
                    url += "/";
                    return url.Substring(0, m_rootFolder.Length) == m_rootFolder;
                }
                catch { }
                return false;
            }
        }
 
        public class ListInfoCollection : IEnumerable<ListInfo>
        {
            ListsService.Lists m_listService;
            Dictionary<string, ListInfo> m_lists = new Dictionary<string, ListInfo>();
            public ListInfoCollection(ListsService.Lists listService)
            {
                m_listService = listService;
            }
            public IEnumerator<ListInfo> GetEnumerator()
            {
                return m_lists.Values.GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
            public ListInfo Find(FileInfo fileInfo)
            {
                if (m_lists.ContainsKey(fileInfo.LookupName))
                    return m_lists[fileInfo.LookupName];
                foreach (ListInfo li in m_lists.Values)
                    if (li.IsMatch(fileInfo.LookupName)) return li;
                string webUrl = fileInfo.m_URL;
                if (fileInfo.m_listInfo != null && !string.IsNullOrEmpty(fileInfo.m_listInfo.m_listName))
                {
                    ListInfo listInfo = new ListInfo(CallService(ref webUrl, delegate { return m_listService.GetList(fileInfo.LookupName); }));
                    listInfo.m_webUrl = webUrl;
                    return listInfo;
                }
                else
                {
                    XmlNode lists = CallService(ref webUrl, delegate { return m_listService.GetListCollection(); });
                    if (lists == null) throw new Exception("Could not find web.");
                    //Find list by RootFolder (which doesn't seem to be populated in GetListCollection response so must iterate GetList response)
                    foreach (XmlNode list in lists.ChildNodes)
                    {
                        ListInfo listInfo = new ListInfo(m_listService.GetList(list.Attributes["Name"].Value));
                        listInfo.m_webUrl = webUrl;
                        m_lists.Add(listInfo.m_listName, listInfo);
                        if (listInfo.IsMatch(fileInfo.LookupName))
                            return listInfo;
                    }
                }
                throw new Exception("Could not find list.");
            }
            private delegate XmlNode ServiceOperation();
            private XmlNode CallService(ref string webURL, ServiceOperation serviceOperation)
            {
                try
                {
                    webURL = webURL.Substring(0, webURL.LastIndexOf("/"));
                    try
                    {
                        m_listService.Url = webURL + "/_vti_bin/Lists.asmx";
                        return serviceOperation();
                    }
                    catch
                    {
                        return CallService(ref webURL, serviceOperation);
                    }
                }
                catch
                {
                    webURL = null;
                    return null;
                }
            }
        }
 
        public class FileInfo
        {
            public string m_URL;
            public byte[] m_bytes;
            public Dictionary<string, object> m_properties;
            public ListInfo m_listInfo;
            public bool m_ensureFolders = true;
            private Uri m_uri;
            public bool HasProperties
            {
                get { return m_properties != null && m_properties.Count > 0; }
            }
            public string RelativeFilePath
            {
                get { return m_URL.Substring(m_URL.IndexOf(m_listInfo.m_rootFolder) + 1); }
            }
            public Uri URI
            {
                get
                {
                    if (m_uri == null) m_uri = new Uri(m_URL);
                    return m_uri;
                }
            }
            public string LookupName
            {
                get
                {
                    if (m_listInfo != null && !string.IsNullOrEmpty(m_listInfo.m_listName))
                        return m_listInfo.m_listName;
                    return URI.LocalPath;
                }
            }
            public FileInfo(string url, byte[] bytes, Dictionary<string, object> properties)
            {
                m_URL = url.Replace("%20", " ");
                m_bytes = bytes;
                m_properties = properties;
            }
        }
 
        public bool Upload(string destinationUrl, byte[] bytes, Dictionary<string, object> properties)
        {
            return Upload(new FileInfo(destinationUrl, bytes, properties));
        }
 
        public bool Upload(FileInfo fileInfo)
        {
            if (fileInfo.HasProperties)
                fileInfo.m_listInfo = m_lists.Find(fileInfo);
            bool result = TryToUpload(fileInfo);
            if (!result && fileInfo.m_ensureFolders)
            {
                string root = fileInfo.URI.AbsoluteUri.Replace(fileInfo.URI.AbsolutePath, "");
                for (int i = 0; i < fileInfo.URI.Segments.Length - 1; i++)
                {
                    root += fileInfo.URI.Segments[i];
                    if (i > 1) CreateFolder(root);
                }
                result = TryToUpload(fileInfo);
            }
            return result;
        }
 
        private bool TryToUpload(FileInfo fileInfo)
        {
            try
            {
                WebRequest request = WebRequest.Create(fileInfo.m_URL);
                request.Credentials = m_credentials;
                request.Method = "PUT";
                byte[] buffer = new byte[1024];
                using (Stream stream = request.GetRequestStream())
                using (MemoryStream ms = new MemoryStream(fileInfo.m_bytes))
                    for (int i = ms.Read(buffer, 0, buffer.Length); i > 0; i = ms.Read(buffer, 0, buffer.Length))
                        stream.Write(buffer, 0, i);
                WebResponse response = request.GetResponse();
                response.Close();
                if (fileInfo.HasProperties)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append("<Method ID='1' Cmd='Update'><Field Name='ID'/>");
                    sb.AppendFormat("<Field Name='FileRef'>{0}</Field>", fileInfo.m_URL);
                    foreach (KeyValuePair<string, object> property in fileInfo.m_properties)
                        sb.AppendFormat("<Field Name='{0}'>{1}</Field>", property.Key, property.Value);
                    sb.Append("</Method>");
                    System.Xml.XmlElement updates = (new System.Xml.XmlDocument()).CreateElement("Batch");
                    updates.SetAttribute("OnError", "Continue");
                    updates.SetAttribute("ListVersion", fileInfo.m_listInfo.m_version);
                    updates.SetAttribute("PreCalc", "TRUE");
                    updates.InnerXml = sb.ToString();
                    m_listService.Url = fileInfo.m_listInfo.m_webUrl + "/_vti_bin/Lists.asmx";
                    XmlNode updatesResponse = m_listService.UpdateListItems(fileInfo.m_listInfo.m_listName, updates);
                    if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000")
                        throw new Exception("Could not update properties.");
                }
                return true;
            }
            catch (WebException)
            {
                return false;
            }
        }
 
        private bool CreateFolder(string folderURL)
        {
            try
            {
                WebRequest request = WebRequest.Create(folderURL);
                request.Credentials = m_credentials;
                request.Method = "MKCOL";
                WebResponse response = request.GetResponse();
                response.Close();
                return true;
            }
            catch (WebException)
            {
                return false;
            }
        }
    }
}

69 comments:

Anonymous said...

What's wrong with HTTP "PUT"?

txs8311 said...

Nothing - the code above does use HTTP PUT to write the file, and if a simple write is all you want to do then you're right - that's all you need.

If you want to write meta data though you have to do a bit more than that. The code above goes a step beyond that, because it discovers the document library name from the URL, creates any folders, and persists the list information so it doesn't have to re-query the web service.

Higo said...

It works for a document library, like intended.
But is it possible to make it work with a custom list? I've tried it it, but no success.

Higo said...

! The code DOES seem to work , because i can see the file in the custom list. But i do get an error: 0x81020016 wich seems to mean that it thinks the file does not exist.

So if i just ignore this "error" it should work.

txs8311 said...

Yeah, could be a bit dodgy. For custom lists, you might want to take a look at the AddAttachment operation that the Lists web service provides. It won't work for uploading files to document libraries but it might be what you're after.

Mike Smith said...

Oooh, I've been looking for something like this, but what I'd really like is to be able to post a chunk of HTML up to a specified Sharepoint Wiki page. How would one go about that?

Anonymous said...

Hey!

I use your code in a case. Everything works fine with doc files. But when I try to upload a docx file some bytes are lost and the document gets corrupted. Please help!

Toni said...

Hi!

Can you please test this with docx and doc documents with som images in it?

txs8311 said...

I tested for docx and it works ok for me, e.g.:

docLibHelper.Upload("http://localhost/Docs/Test Folder/Test.docx", File.ReadAllBytes(@"D:\Temp\Test.docx"), properties);

If all the bytes are getting uploaded, I would guess it's a machine related issue. Try testing on a different machine or user profile. Clearing your temporary internet files can sometimes fix browser problems. Also, there's a second post (Part II) that uses a different technique for uploading if you're convinced it's the code in this example.

Toni said...

Part II worked fine with uploading both docx and doc.
Now I have problem with uploading the metadata with docx in Part II. With doc everything workes fine.
Thank you for your help.

Anonymous said...

Receiving error 0x80070005 (Error occurred when updating list.)

I have admin privledges, so I wouldn't expect it to be a security issue, but everything I've read points to that...Any ideas?

Srinivasan said...

hi

iam getting an error like file not found(404) . i tried to upload a document to document library and even when i try to create a folder , i got the same error. can you help me in fixing this ?

Sandeep said...

Hi,

Great code! I noticed that the version history is incremented by a version once the file metadata has been added. It would be nice if when uploading it would keep the same version when the initital file metadata is added (i.e. for custom content types/columns on the document library). Do you think that this is actually possible using your code?

Sean Moran said...

First let me say that I appreciate this code a lot. Now the problem:

I am trying to hit a server elsewhere in out environment. I am creating a new NetworkCredential and passing in args that work when I enter them manually to hit my SharePoint site.

However, when I try to use them TryToUpload method I get an error at

WebResponse response = request.GetResponse();

The error is:


base {System.InvalidOperationException} = System.InvalidOperationException

base {object} = object
_COMPlusExceptionCode = -532459699

This appears to be a security issue.

Any thoughts? The credentials work fine to interrogate the lists and such. It seems to be tripping up on the upload.

Thanks,

Sean

txs8311 said...

Sean - I'm not sure that I've come across that one before. Someone posting here seemed to have a similar issue - perhaps adding a line like -

request.Proxy = m_listService.Proxy;

immediately after the line

request.Credentials = m_credentials;

- might help. If not, take a look at the RPC code posted in Part II of this article for an alternative remote upload method.

bobby said...

Have anyone got the VB version of this code.

André Rentes said...

Hi, very good code!!! But I have a problem! When I try upload a file with parameters I received the error "0x80070005" The file was upload, but my fields are not updated.

txs8311 said...

André - error code 0x80070005 can indicate that the user credentials passed to the lists web service do not have rights to update the list.

Frank Bell said...

Hey to start I want to say Thanks A LOT for this post it really helped me out getting something going.

If you have time, I was wondering what I would have to do to remove the "overwrite" fuctionality. If the file already exists, I need it to error out.

Thanks again.

txs8311 said...

Frank,

Take a look at the If-None-Match request header.

e.g.:

request.Method = "PUT";
request.Headers.Add("If-None-Match", "*");

Alternatively, the RPC method in Part II of this post includes an overwrite "putOption".

NoJ said...

Nice idea and good coding!
I used another hack for uploading to a DocLib using existing Web Services:
1. Upload the file to an ImageLib with Imaging.Upload
2. Copy/Move the file with Copy.CopyIntoItemsLocal

since it uses existing Web Services, it only needs ~70 lines of code.

Source and sample project is available at http://blog.janus.cx/archives/251-Uploading-any-file-to-a-SharePoint-DocumentLibraray-using-WebServices.html

Anonymous said...

Great post!

I'm following the basic structure of the article to upload to a document library via HTTP PUT. It works great.

Here's one problem I'm having though ... if versioning is turned on for the document library AND require check out is set to yes for the document library, then after the doc is uploaded it remains checked out. See this URL for more details:

http://sharemypoint.wordpress.com/2007/12/20/automatically-checkin-files-after-uploading/

So you think solving this problem would be easy -- just use the Lists.CheckInFile web service call to execute the check in. However, this is not working. WSS is returning the following error:

The system cannot find the file specified. (Exception from HRESULT: 0x80070002)

After playing around some more, if I take the URL to the newly uploaded file (the same one I pass to CheckInFile) and paste that URL into a new browser window, the browser window returns a 404 PAGE NOT FOUND error. However, if open a new browser window, login to my SharePoint site, *then* paste the URL into the browser window, the document opens fine.

Thus, for new documents that have yet to be checked in, SP is apparently throwing a 404 unless you are already logged in. Thus, the CheckInFile web service call must be failing for the same reason.

Other ways you can think of to work around this? Can check in be done via a RPC call or something?

Here's what I'm after:

- Configure doc library to enable versioning (major OR minor/major) and Require Check Out = "yes"
- Upload document via HTTP PUT, as outlined in your post
- Immediately after upload, check document in

Thanks in advance!

txs8311 said...

Yep - check-in can be done via FrontPage Server Extensions RPC calls (see the checkin document method on MSDN).

Check out the post below - you can download code from our site that wraps most of the FPSE methods and provides additional functionality.

http://geek.hubkey.com/2008/07/programatically-manage-remote-documents.html

Mike said...

Hello!

Just wanted to start by saying Great Work! and thanks for sharing.

If you have some time, I was wondering what this was doing --

if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000") throw new Exception("Could not update properties.");


I get that exception randomly from users. Some times they can close out and redo it and it works. So, I'm guessing that "0x00000000" means that it was successful. But, is there anyway to determine why it wasn't?

Thanks a lot!

-Mike

txs8311 said...

Mike, yep error code 0 means success. You can get a description of the error code by parsing the returned xml for the ErrorText element if the error code is non-zero, e.g.

throw new Exception(updatesResponse.FirstChild.FirstChild.NextSibling.InnerText);

Ben said...

Hi, It is working great! But i have one question: how do i fill in metadata for datetime fields?

txs8311 said...

Ben, format your DateTime fields in the ISO 8601 format with a time zone designator for the zero UTC offset after it (a 'Z'). The DateTime field should in the local time zone (i.e. not actually converted to UTC).

e.g.

properties.Add("dateandtime", DateTime.Now.ToString("s") + "Z");

j said...

This is great. Thank you!!!

APG said...

I am new to development with Sharepoint. Why would it be beneficial to write to solve the upload problem INDEPENDENT OF THE SHAREPOINT API?

txs8311 said...

apg - the API is implemented through dlls that are installed on the server only. Even if you have the dlls installed on a client machine, you'll receive a FileNotFoundException when trying to connect to a remote server. This is because the "SharePoint object model can only be used when the application is run on a server in the SharePoint farm. It cannot be executed remotely." (See the first problem resolution in the SharePoint Development and Programming FAQ.)

An example of uploading a file that resides locally on the server using the API can be found here.

Juan Alvarez said...

Thanks a lot, it worked with Doc Libs but i have a problem with Custom Lists. I just have one field "Title", when i had the property it returns:

"0x81020016Item does not exist\n\nThe page you selected contains an item that does not exist. It may have been deleted by another user."

I need help over here, how can a page "COUNTAINS" something that does not exist :S

txs8311 said...

Juan - this code will only work with document libraries. We'll be posting about an easy way to remotely update SharePoint lists in the near future. Thanks.

Anonymous said...

The code works, but I haven`t understood why some files are well uploaded, but another - otherwise!
The code retruns me the error 409, when WebResponse response = request.GetResponse(); was executing! Could you please make me clear in this problem?

Anonymous said...

What a great solution! Everything worked on first try! Thanks, it saved my day!

Ramesh Krishnan said...

Great Piece of code. Thank you

I've a question. If I want to retain Id, Create, Modified, Author and Editor how would I do that. I tried sending as a properties but, it didn't seems to be working.

Any help will be appreciated

txs8311 said...

Hi Ramesh,

Take a look at my reply to the first comment in Part II of this post - hopefully that'll point you in the right direction re authorship. ID is an uniquifier assigned by SharePoint so you won't be able to specify that - the creation date/time is also written by SharePoint.

Ramesh Krishnan said...

Thanks Txs8311 for the quick reply.

hmm. if we do the content migration they are able to retain all the info. Is it possible if I do a OM then?

Also, for the custom list I made small change to the code so that it updates custom list bymaking changes to TrytoUpload as follows

private bool TryToUpload(FileInfo fileInfo)
{
try
{
WebRequest request = WebRequest.Create(fileInfo.m_URL);
request.Credentials = m_credentials;
request.Method = "PUT";
if (fileInfo.m_bytes != null)
{
byte[] buffer = new byte[1024];
using (Stream stream = request.GetRequestStream())
using (MemoryStream ms = new MemoryStream(fileInfo.m_bytes))
for (int i = ms.Read(buffer, 0, buffer.Length); i > 0; i = ms.Read(buffer, 0, buffer.Length))
stream.Write(buffer, 0, i);
WebResponse response = request.GetResponse();
response.Close();

if (fileInfo.HasProperties)
{
StringBuilder sb = new StringBuilder();

'removed the code for the fields here


System.Xml.XmlElement updates = (new System.Xml.XmlDocument()).CreateElement("Batch");
updates.SetAttribute("OnError", "Continue");
updates.SetAttribute("ListVersion", fileInfo.m_listInfo.m_version);
updates.SetAttribute("PreCalc", "TRUE");
updates.InnerXml = sb.ToString();
m_listService.Url = fileInfo.m_listInfo.m_webUrl + "/_vti_bin/Lists.asmx";
XmlNode updatesResponse = m_listService.UpdateListItems(fileInfo.m_listInfo.m_listName, updates);
if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000")
throw new Exception("Could not update properties.");
}
}
else
{
UpdateListItem(fileInfo);
}
return true;
}
catch (WebException)
{
return false;
}
}


//New Method to update the custom list

private bool UpdateListItem(FileInfo fileInfo)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
System.Xml.XmlElement batchElement = doc.CreateElement("Batch");
batchElement.SetAttribute("OnError", "Continue");
batchElement.SetAttribute("ListVersion", "1");
batchElement.SetAttribute("PreCalc", "TRUE");
//batchElement.SetAttribute("ViewName", strViewID);

StringBuilder sb = new StringBuilder();

'removed code for the method


batchElement.InnerXml = sb.ToString();
m_listService.Url = fileInfo.m_listInfo.m_webUrl + "/_vti_bin/Lists.asmx";
XmlNode updatesResponse = m_listService.UpdateListItems(fileInfo.m_listInfo.m_listName, batchElement);
if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000")
throw new Exception("Could not update properties.");
return true;

}


and when you call Upload pass null for the File bytes

docLibHelper.Upload("http://localhost/NewSite/Lists/" + listName, null, properties);

Thanks
Ramesh

Anonymous said...

Hi,
Please help a newbie: When I use VS2008, should I create a ServiceReference or a webreference to make this work?
No matter what I try, I get this:
(It is driving me nuts!)

"The type or namespace name 'ListsService' could not be found (are you missing a using directive or an assembly reference?) D:\SP_uploader_using_web_services\SP_uploader_using_web_services\Program.cs 69 13 SP_uploader_using_web_services
"
Thanks in advance!
/Björn

txs8311 said...

Hey Björn,

When you create the Web Reference to the SharePoint Lists service (/_vti_bin/Lists.asmx) and name it ‘ListsService’, VS2008 automatically generates code with the default namespace (e.g. ConsoleApplication1) for your project (to change this, see the properties tab - right click on your project in the solution explorer). Couple of easy fixes: either change the default namespace to DevHoleDemo before adding the reference, or change the namespace from DevHoleDemo to your default namespace. You can also view the code that is generated when the reference is added by clicking the Show All Files button in the solution explorer, and then expanding the web references tree until you get to Reference.cs - you'll be able to see what namespace is being used in the generated code there.

treehugger said...

Thanks, this is very useful. One question, when I add lists.asmx as a service reference in a test dialog app in vs2008 it says it cannot find "Lists", I'm using MOSS2007?

Howy said...

Hi Ramesh!
Great application! I learned a lot when studying your source code!

Unfortunately I get the same error 409 when using the web request.
Can you give me a hint?

I also tried it with adminstrative credentials - no difference. The URL seemed to be OK, too.

Kind regards,
Howy

Anonymous said...

Sharepoint noob here. How do I use this code to upload all files in a local directory to a document list?

txs8311 said...

Something like this:

static void Main(string[] args)
{
DocLibHelper docLibHelper = new DocLibHelper();
DirectoryInfo di = new DirectoryInfo(@"C:\Temp");
foreach (FileInfo fi in di.GetFiles())
docLibHelper.Upload(string.Format("http://localhost/Docs/{0}", fi.Name), File.ReadAllBytes(fi.FullName), null);
}

Jonathan said...

Does this code support all column data types? If so, what is the format that they should be passed in?

For example, I'm having a problem with People column type in my code. Is it as simple as "DOMAIN\\User"?

Please send me a message at jonathancifuentes(at)live.com
if you know.

Jason said...

Interestingly, if you upload a file where the document library does not exist you do not get an error. Something DOES get uploaded because if you then create the library, the url of it has a 1 appended (as if it found a conflicting name). Weird.

Yenkay said...

Hi,
I tried uploading a document using this class, the document get uploaded with no issues. But how can I get the Id of the document uploaded. On the updateRespose i can only see ErrorCode node. No z:row node is returned.

Anonymous said...

Hi txs8311 and Björn,
I get the same error as the "The type or namespace name 'UserProfileService' does not exist in the namespace 'CreateUserProfile.UserProfileService' (are you missing an assembly reference?)"

Can you pl elaborate what you meant by looking into the Reference.cs file. I don't think I understood properly.
Thanks.
Zullu

txs8311 said...

Hi Zullu,

This looks a bit off topic, but you're trying to use the UserProfileService web service?

If so, take a look at the following code:

using System;
using System.Collections.Generic;
using System.Text;
using ConsoleApplication1.CreateUserProfile;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UserProfileService proxy = new UserProfileService();
PropertyData[] d = proxy.CreateUserProfileByAccountName("newAccountName");
}
}
}

I added the namespace ConsoleApplication1.CreateUserProfile with the line "using ConsoleApplication1.CreateUserProfile;"

This will include the code that was generated automatically when I added a reference to the web service. If you show all files in the VS solution explorer (use the show all files button at the top) and then expand the reference until you see the code (reference.cs) you'll see the namespace you need to add.

Hope this helps.

Anonymous said...

txs8311,
Thanks for your prompt reply.
Yes, I am using the UserProfileService web service. But here I have the same problem as any other web service.
I understand what you are trying to explaing here with your code. I already have that line "using CreateUserProfile.UserProfileService;" in my code, but I still get the same error.
I am not sure why you are not a compile error while instantiating the "proxy" in line:
UserProfileService proxy = new UserProfileService();
I am stuck, can you provide some more help.
Thanks in advance.
Zullu

aidandegraaf said...

Hey! Has anyone had any trouble with file overwrite using this method? I have implemented this technique in my own document provider, however once I've uploaded once, any subsequent uploads do not overwrite. No exceptions are thrown, and I can step through the whole process, however the same file remains in WSS afterwards. How can this be?!

Yenkay said...

Hi,
I used your file uploading code and it is working like a charm in MOSS2007/Win2003 environment. But when I tried the same thing in MOSS2007/Win2008/Vista environment, the Upload function throws "405 Method Not Allowed" error. Is there anything I have to change in the code in order to work with MOSS2007 hosted on Win2008.
Thanks
NK

aacinc said...

Hi!
I tried the solution but every time I run I receive a catch that says that it cannot find the list.
I have tried different variations of the list but cannot get by the message.
I changed the sharepoint location to my own and put in a structure that didn't exist. Other than that there are no changes.

Whate else might be wrong.

Kostya Afendikov said...

Hi, can you help me
How upload file from sharepoint to ms crm 4 annotations for example?
I have full url to file

Tracy said...

It was bugging out with bigger files, so I switched the byte[] to a FileStream derived from File.OpenRead(filepath).. Hope that helps someone else!

pwr said...

Great post... Also agree with the comment about swithing the code to make use of file stream rather than byte array...

Malcolm said...

A few people have noted that they cannot find the Lists object in their ListService object.

Error: The type or namespace name 'Lists' does not exist in the namespace 'ListsService' (are you missing an assembly reference?)

Make sure you add Lists.asmx service as a Web Reference, not a Service Reference. This MSDN article on Web Service Guidelines sheds some light on how to do that. http://msdn.microsoft.com/es-es/library/ms458094.aspx. Scroll down to Guidelines for Using the ASP.NET Web Services.

1. Go to Add Service Reference Dialog.
2. Click Advanced... to open Service Reference Settings
3. Click Add Web Reference
4. In Add Web Reference dialog box, enter your web service url, click green arrow, set your Web Reference name and click Add Reference.

3.

Vidya said...

When uploading a document from CRM to SharePoint, the first version of the document is 2. This is because versioning is turned on and when you upload the document it is Version 1 and when you update the Meta data it is considered as Version 2. Is there a solution for this issue. I want the version to remain as 1.

Anonymous said...

The code works just fine. However, I received an error "Could not locate list."

Some digging showed me that in your IsMatch method of your list service has this line:

return url.Substring(0, m_rootFolder.Length) == m_rootFolder;

If the first parameter used in the DocLibHelper.Upload method isn't the same case as the list name, I got an error, so I changed it to this:

return url.Substring(0, m_rootFolder.Length).ToLower() == m_rootFolder.ToLower();

which meant I was always comparing the same case. Hope this helps someone.

Quentin said...

Thanks for the great code! I have found it works well when used with a MOSS2007 server. However, when I try to implement it into a MOSS2010 environment I come across a problem. Everything seems to work fine until it comes time to update the metadata within the 'TryToUpload' function. The file uploads fine but when it tries to update the data at 'UpdateListItems' I get the error that the file doesn't exist when in fact it does.
Has anyone had this problem? Any clues...PLEASE!!!

Thanks everyone.

Quentin said...

Further to my previous post, more correctly, the error I get is...

"The URL 'Doc Library/899/SJMSCL5EUBQ.pdf' is invalid. It may refer to a nonexistent file or folder, or refer to a valid file or folder that is not in the current Web."

If you open Sharepoint you can actually see the file and the path is correct.

jeremy said...

On 2007, this code works flawlessly. On a SharePoint 2010 platform, was having same issues as last poster with failure to update errors.

Using the copy service as outlined in the solution on this page:
http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/f135aaa2-3345-483f-ade4-e4fd597d50d4

documents are uploaded with properties. Need to figure out how to remove the linked copy flag when viewing the item properties... But the linked solution will get documents up into 2010.

Quentin said...

Earlier in the month I had posted about a problem with updating column data in MOSS2010 (where it had worked fine in 2007). The cause was that 2010 requires the doc ID where 2007 was happy with just a doc reference.

To fix this, I added a function to get the doc id and then added this to the update xml.

The function i added was...

Private Function GetDocID(ByVal s_URL As String) As String
Dim m_sitedataService As New svc_sharepoint_sitedata.SiteData
Dim s_DocPath As String
s_DocPath = Replace(s_URL, Path.GetFileName(s_URL), "")
m_sitedataService.Url = s_DocPath & "_vti_bin/sitedata.asmx"
m_sitedataService.Credentials = m_credentials

Dim s_webId As String = String.Empty
Dim s_listId As String = String.Empty
Dim s_itemId As String = String.Empty
Dim s_bucketId As String = String.Empty

Dim ret As Boolean = m_sitedataService.GetURLSegments(s_URL, s_webId, s_bucketId, s_listId, s_itemId)

Return s_itemId

End Function

and then I just added a call to it just before "Dim sb As New StringBuilder()"...

Dim DocGUID As String = GetDocID(fileInfo.m_URL)

Now we just add the DocGUID to the ID field.

Now it all works sweet with SP 2010!

Q.

CMO said...

Great article
Thank you Kevin for your code for 2010.
I had the same issue...

Anonymous said...

I referenced the webservice and named it ListsService

However I cannot find a type
ListsService.Lists

I only see ListsService.ListsSoapChannel

Please help. Thank you.

vnroopa@gmail.com said...

hi,
Please help this newbee..

I am getting an error "couldnot update properties" while uploading a document into a document library in sharepoint 2007.

Please find the code below.

if (fileInfo.HasProperties)
{

StringBuilder sb = new StringBuilder();

sb.Append("");

sb.AppendFormat("{0}", fileInfo.m_URL);

foreach (KeyValuePair property in fileInfo.m_properties)

sb.AppendFormat("{1}", property.Key, property.Value);

sb.Append("");


Please help me.

GeethaPriya said...

Love the post... it a wonderful piece of work.. Thank you so much.. it helped me a ton...

@vnroopa@gmail.com :

I am sure you would have fixed the error by now.. if anybody else faces the issue :

Check if the column names to which you are trying to update values are right. If it is a custom column, then sharepoint internally appends 00x20 in between for the spaces. Open the site in the sharepoint designer,check for the column name and put that here... it works like a charm..

JayaRaja said...

just a quick query... the code you have given.. first uploads the doc and then updates the meta data where it creates new version of the doc... is there a way tat i can upload the doc with the properties inserted together without creating a new version ???

Krisje said...

Hi All,

First of all: What a GREAT post !

I also had the nonexistent file or folder error in 2010 but managed to solve this easy by adding the doc ID (which I retrieved from SiteData)

3 steps to follow:
- add SiteData webreference (_vti_bin/SiteData.asmx)
- Change code where Field TAG is set :
Field Name='ID'>" + GetDocID(fileInfo.m_URL) + "< /Field>"
-add method getDocID:

private String GetDocID(String sUrl)
{

String sWebId,sBucketId,sListId;
String sReturn = "";
DevHoleDemo.SiteData.SiteData data = new DevHoleDemo.SiteData.SiteData();

data.Credentials = m_credentials;
try
{
data.GetURLSegments(sUrl, out sWebId, out sBucketId, out sListId, out sReturn);
}
catch (Exception e)
{

}


return sReturn;
}

Bruce Lowery said...

It would be nice if when uploading it would keep the same version when the initial file metadata is added.