SharePoint 2010 - Client Object Model - Add attachment to ListItem
Asked Answered
U

6

14

I have a SharePoint List to which I'm adding new ListItems using the Client Object Model. Adding ListItems is not a problem and works great.

Now I want to add attachments.

I'm using the SaveBinaryDirect in the following manner:

File.SaveBinaryDirect(clientCtx, url.AbsolutePath + "/Attachments/31/" + fileName, inputStream, true);

It works without any problem as long as the item that I'm trying to add the attachment to, already has an attachment that was added through the SharePoint site and not using the Client Object Model.

When I try to add an attachment to a item that doesnt have any attachments yet, I get the following errors (both happen but not with the same files - but those two messages appear consistently):

The remote server returned an error: (409) Conflict
The remote server returned an error: (404) Not Found

I figured that maybe I need to create the attachment folder first for this item. When I try the following code:

clientCtx.Load(ticketList.RootFolder.Folders);
clientCtx.ExecuteQuery();
clientCtx.Load(ticketList.RootFolder.Folders[1]);             // 1 -> Attachment folder
clientCtx.Load(ticketList.RootFolder.Folders[1].Folders);
clientCtx.ExecuteQuery();
Folder folder = ticketList.RootFolder.Folders[1].Folders.Add("33");
clientCtx.ExecuteQuery();

I receive an error message saying:

Cannot create folder "Lists/Ticket System/Attachment/33"

I have full administrator rights for the SharePoint site/list.

Any ideas what I could be doing wrong?

Thanks, Thorben

Unhallowed answered 3/6, 2010 at 21:9 Comment(0)
S
9

I have discussed this question with Microsoft. Looks like that only one way to create attachments remotely is List.asmx web service. I have tried to create this subfolder also and with no success.

Showery answered 17/6, 2010 at 16:5 Comment(2)
Thanks for the answer, MaxBeard. At least knowing that it is not possible is better than trying & failing - it seems like it ought to be possible. By the way, nice blog.Unhallowed
This does indeed seem to be the case... see my answer for a full code sample of how to accomplish this.Xenolith
X
12

I struggled for a long time with this problem too, so I thought I'd post a complete code sample showing how to successfully create a list item and add an attachment.

I am using the Client Object API to create the list item, and the SOAP web service to add the attachment. This is because, as noted in other places on the web, the Client Object API can only be used to add attachments to an item where the item's upload directory already exists (eg. if the item already has an attachment). Else it fails with a 409 error or something. The SOAP web service copes with this OK though.

Note that another thing I had to overcome was that even though I added the SOAP reference using the following URL:

https://my.sharepoint.installation/personal/test/_vti_bin/lists.asmx

The URL that VS actually added to the app.config was:

https://my.sharepoint.installation/_vti_bin/lists.asmx

I had to manually change the app.config back to the correct URL, else I would get the error:

List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user. 0x82000006

Here is the code:

    void CreateWithAttachment()
    {
        const string listName = "MyListName";
        // set up our credentials
        var credentials = new NetworkCredential("username", "password", "domain");

        // create a soap client
        var soapClient = new ListsService.Lists();
        soapClient.Credentials = credentials;

        // create a client context
        var clientContext = new Microsoft.SharePoint.Client.ClientContext("https://my.sharepoint.installation/personal/test");
        clientContext.Credentials = credentials;

        // create a list item
        var list = clientContext.Web.Lists.GetByTitle(listName);
        var itemCreateInfo = new ListItemCreationInformation();
        var newItem = list.AddItem(itemCreateInfo);

        // set its properties
        newItem["Title"] = "Created from Client API";
        newItem["Status"] = "New";
        newItem["_Comments"] = "here are some comments!!";

        // commit it
        newItem.Update();
        clientContext.ExecuteQuery();

        // load back the created item so its ID field is available for use below
        clientContext.Load(newItem);
        clientContext.ExecuteQuery();

        // use the soap client to add the attachment
        const string path = @"c:\temp\test.txt";
        soapClient.AddAttachment(listName, newItem["ID"].ToString(), Path.GetFileName(path),
                                  System.IO.File.ReadAllBytes(path));
    }

Hope this helps someone.

Xenolith answered 27/7, 2012 at 9:45 Comment(2)
I'm using Visual Studio 2012 and attempting to save list item attachments to SharePoint 2010. I cannot find any web-services on my SharePoint 2010 server whose proxy classes and methods match the example above.At
Ok, I found my problem. In Visual Studio 2012, one has to add a Web Reference rather than a Service Reference. From the "Add Service Reference" dialog box, click the "Advanced..." button. Then click the "Add Web Reference..." button. Now the familiar "Add Web Reference" dialog box from earlier versions of Visual Studio is displayed.At
S
9

I have discussed this question with Microsoft. Looks like that only one way to create attachments remotely is List.asmx web service. I have tried to create this subfolder also and with no success.

Showery answered 17/6, 2010 at 16:5 Comment(2)
Thanks for the answer, MaxBeard. At least knowing that it is not possible is better than trying & failing - it seems like it ought to be possible. By the way, nice blog.Unhallowed
This does indeed seem to be the case... see my answer for a full code sample of how to accomplish this.Xenolith
P
6

With Sharepoint 2010 there was no way to upload a first attachment to a list item using the COM. The recommendation was to use the Lists web service inmstead.

With Sharepoint 2013 it works.

AttachmentCreationInformation newAtt = new AttachmentCreationInformation();
newAtt.FileName = "myAttachment.txt";
// create a file stream
string fileContent = "This file is was ubloaded by client object meodel ";
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] buffer = enc.GetBytes(fileContent);
newAtt.ContentStream = new MemoryStream(buffer);

// att new item or get existing one
ListItem itm = list.GetItemById(itemId);
ctx.Load(itm);   
// do not execute query, otherwise a "version conflict" exception is rised, but the file         is uploaded
// add file to attachment collection
newAtt.ContentStream = new MemoryStream(buffer);
itm.AttachmentFiles.Add(newAtt); 
AttachmentCollection attachments = itm.AttachmentFiles;
ctx.Load(attachments);
ctx.ExecuteQuery(); 
// see all attachments for list item
// this snippet works if the list item has no attachments

This method is used in http://www.mailtosharepoint.net/

Pharmacognosy answered 17/6, 2013 at 9:14 Comment(3)
Wow thank you so much. This works great with SharePoint (2013) Online and a MVC App.Rooky
@Pharmacognosy Unless I'm missing something, this does not appear to work unless you are using an existing ListItem. If you are creating a new item via the AddItem method, this does not work. It's throwing an error saying that you must first save the item, then save the attachment.Blackleg
@sam, I take it back, it does work :) but not quite the way you have it described in your snippet. You do need to add the item first calling the AddItem, then call code from GetItemById and it does work very nicely.Blackleg
B
4

It reflects rather poorly on the Microsoft SharePoint team for not coming forward with an acknowledgement of the issue and a usable suggestion on how to resolve it. Here is how I dealt with it:

I am using the new SharePoint 2010 managed client that ships with the product. Hence, I already have a SharePoint ClientContext with credentials. The following function adds an attachment to a list item:

private void SharePoint2010AddAttachment(ClientContext ctx, 
                                     string listName, string itemId, 
                                     string fileName, byte[] fileContent)
{
    var listsSvc = new sp2010.Lists();
    listsSvc.Credentials = _sharePointCtx.Credentials;
    listsSvc.Url = _sharePointCtx.Web.Context.Url + "_vti_bin/Lists.asmx";
    listsSvc.AddAttachment(listName, itemId, fileName, fileContent);
}

The only prerequisite for the code above is to add to the project (I used Visual Studio 2008) a _web_reference_ I called sp2010 which is created from the URL of: http:///_vti_bin/Lists.asmx

Bon Chance...

Burdensome answered 16/5, 2011 at 20:29 Comment(0)
T
0

I've used and tried this one on my CSOM (SharePoint Client Object Model) application and it works for me

using (ClientContext context = new ClientContext("http://spsite2010"))
                {

                    context.Credentials = new NetworkCredential("admin", "password");
                    Web oWeb = context.Web;
                    List list = context.Web.Lists.GetByTitle("Tasks");
                    CamlQuery query = new CamlQuery();
                    query.ViewXml = "<View><Where><Eq><FieldRef Name = \"Title\"/><Value Type=\"String\">New Task Created</Value></Eq></Where></View>";
                    ListItemCollection listItems = list.GetItems(query);
                    context.Load(listItems);
                    context.ExecuteQuery();
                    FileStream oFileStream = new FileStream(@"C:\\sample.txt", FileMode.Open);
                    string attachmentpath = "/Lists/Tasks/Attachments/" + listItems[listItems.Count - 1].Id + "/sample.txt";
                    Microsoft.SharePoint.Client.File.SaveBinaryDirect(context, attachmentpath, oFileStream, true);
                }

Note: Only works if item folder has been created already

Tycoon answered 16/11, 2012 at 7:29 Comment(0)
K
0

HTML:

<asp:FileUpload ID="FileUpload1" runat="server" AllowMultiple="true" />

Event in code behind :

protected void UploadMultipleFiles(object sender, EventArgs e)
{
    Common.UploadDocuments(Common.getContext(new Uri(Request.QueryString["SPHostUrl"]),
    Request.LogonUserIdentity), FileUpload1.PostedFiles, new CustomerRequirement(), 5);
}

public static List<string> UploadDocuments<T>(ClientContext ctx,IList<HttpPostedFile> selectedFiles, T reqObj, int itemID)
{
    List<Attachment> existingFiles = null;
    List<string> processedFiles = null;
    List<string> unProcessedFiles = null;
    ListItem item = null;
    FileStream sr = null;
    AttachmentCollection attachments = null;
    byte[] contents = null;
    try
    {
        existingFiles = new List<Attachment>();
        processedFiles = new List<string>();
        unProcessedFiles = new List<string>();
        //Get the existing item
        item = ctx.Web.Lists.GetByTitle(typeof(T).Name).GetItemById(itemID);
        //get the Existing attached attachments
        attachments = item.AttachmentFiles;
        ctx.Load(attachments);
        ctx.ExecuteQuery();
        //adding into the new List
        foreach (Attachment att in attachments)
            existingFiles.Add(att);
        //For each Files which user has selected
        foreach (HttpPostedFile postedFile in selectedFiles)
        {
            string fileName = Path.GetFileName(postedFile.FileName);
            //If selected file not exist in existing item attachment
            if (!existingFiles.Any(x => x.FileName == fileName))
            {
                //Added to Process List
                processedFiles.Add(postedFile.FileName);
            }
            else
                unProcessedFiles.Add(fileName);
        }
        //Foreach process item add it as an attachment
        foreach (string path in processedFiles)
        {
            sr = new FileStream(path, FileMode.Open);
            contents = new byte[sr.Length];
            sr.Read(contents, 0, (int)sr.Length);
            var attInfo = new AttachmentCreationInformation();
            attInfo.FileName = Path.GetFileName(path);
            attInfo.ContentStream = sr;
            item.AttachmentFiles.Add(attInfo);
            item.Update();
        }
        ctx.ExecuteQuery();
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        existingFiles = null;
        processedFiles = null;
        item = null;
        sr = null;
        attachments = null;
        contents = null;
        ctx = null;

    }
    return unProcessedFiles;
}
Kingcup answered 23/9, 2015 at 7:19 Comment(1)
Just posting a block of code doesn't make a good answer to a question. Please explain how your solution solves the task of the OP and what it adds to the other answers already present on the question.Mansur

© 2022 - 2024 — McMap. All rights reserved.