SharePoint 2010 - 客户端对象模型 - 将附件添加到 ListItem

Posted

技术标签:

【中文标题】SharePoint 2010 - 客户端对象模型 - 将附件添加到 ListItem【英文标题】:SharePoint 2010 - Client Object Model - Add attachment to ListItem 【发布时间】:2011-02-27 12:40:39 【问题描述】:

我有一个 SharePoint 列表,我正在使用客户端对象模型向其中添加新的 ListItem。 添加 ListItems 不是问题并且效果很好。

现在我想添加附件。

我以下列方式使用 SaveBinaryDirect:

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

只要我尝试添加附件的项目已经有一个通过 SharePoint 网站添加的附件并且不使用客户端对象模型,它就可以正常工作。

当我尝试向尚无任何附件的项目添加附件时,我收到以下错误(这两种错误都发生但不是针对相同的文件 - 但这两条消息始终显示):

远程服务器返回错误:(409) Conflict
远程服务器返回错误:(404) Not Found

我想也许我需要先为这个项目创建附件文件夹。 当我尝试以下代码时:

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();

我收到一条错误消息:

无法创建文件夹“Lists/Ticket System/Attachment/33”

我拥有 SharePoint 网站/列表的完全管理员权限。

有什么想法我可能做错了吗?

谢谢,托尔本

【问题讨论】:

【参考方案1】:

我也为这个问题苦苦挣扎了很长时间,所以我想我会发布一个完整的代码示例,展示如何成功创建列表项并添加附件。

我正在使用客户端对象 API 创建列表项,并使用 SOAP Web 服务添加附件。这是因为,正如在网络上的其他地方所指出的,客户端对象 API 只能用于将附件添加到项目的上传目录已经存在的项目(例如,如果项目已经有附件)。否则它会因 409 错误或其他原因而失败。不过 SOAP Web 服务可以解决这个问题。

请注意,我必须克服的另一件事是,即使我使用以下 URL 添加了 SOAP 引用:

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

VS实际添加到app.config的URL是:

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

我必须手动将 app.config 改回正确的 URL,否则我会收到错误消息:

列表不存在。 您选择的页面包含一个不存在的列表。它可能已被其他用户删除。 0x82000006

代码如下:

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

希望这对某人有所帮助。

【讨论】:

谢谢。这是为我做的。 我正在使用 Visual Studio 2012 并尝试将列表项附件保存到 SharePoint 2010。我在我的 SharePoint 2010 服务器上找不到代理类和方法与上述示例匹配的任何 Web 服务。跨度> 好的,我发现了我的问题。在 Visual Studio 2012 中,必须添加 Web Reference 而不是 Service Reference。在“添加服务引用”对话框中,单击“高级...”按钮。然后单击“添加 Web 引用...”按钮。现在会显示旧版 Visual Studio 中熟悉的“添加 Web 引用”对话框。【参考方案2】:

我已与 Microsoft 讨论过这个问题。看起来只有一种远程创建附件的方法是 List.asmx Web 服务。我也尝试创建此子文件夹,但没有成功。

【讨论】:

感谢您的回答,MaxBeard。至少知道这是不可能的总比尝试和失败要好——它似乎应该是可能的。顺便说一句,不错的博客。 这似乎确实是这样...有关如何完成此操作的完整代码示例,请参阅我的答案。【参考方案3】:

在 Sharepoint 2010 中,无法使用 COM 将第一个附件上传到列表项。建议改为使用 Lists Web 服务。

Sharepoint 2013 可以正常工作。

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

这个方法用在http://www.mailtosharepoint.net/

【讨论】:

哇,非常感谢。这适用于 SharePoint (2013) Online 和 MVC 应用程序。 @sam 除非我遗漏了什么,否则这似乎不起作用,除非您使用现有的 ListItem。如果您通过 AddItem 方法创建一个新项目,这不起作用。它抛出一个错误,说您必须先保存项目,然后保存附件。 @sam,我收回它,它确实有效:) 但与您在 sn-p 中描述的方式不同。您确实需要先调用 AddItem 添加项目,然后从 GetItemById 调用代码,它确实工作得很好。【参考方案4】:

这对 Microsoft SharePoint 团队的反映相当糟糕,因为它没有提出对问题的确认和关于如何解决问题的有用建议。以下是我的处理方式:

我正在使用产品随附的新 SharePoint 2010 托管客户端。因此,我已经有一个带有凭据的 SharePoint ClientContext。以下函数将附件添加到列表项:

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

上面代码的唯一先决条件是向项目(我使用 Visual Studio 2008)添加一个我称为 sp2010 的 _web_reference_,它是从以下 URL 创建的:http:///_vti_bin/ Lists.asmx

祝你好运……

【讨论】:

【参考方案5】:

html

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

代码中的事件:

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;

【讨论】:

仅仅发布一段代码并不能很好地回答问题。请解释如何您的解决方案解决了 OP 的任务,以及它对问题中已经存在的其他答案添加了什么。【参考方案6】:

我已经在我的 CSOM(SharePoint 客户端对象模型)应用程序上使用并尝试过这个,它对我有用

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

注意:仅在已创建项目文件夹时才有效

【讨论】:

以上是关于SharePoint 2010 - 客户端对象模型 - 将附件添加到 ListItem的主要内容,如果未能解决你的问题,请参考以下文章

SharePoint 2010 - 客户端对象模型 - 将附件添加到 ListItem

SharePoint 2010客户端对象模型 - 需要代理身份验证

使用客户端对象模型 SharePoint 2010 以编程方式获取 ListItemVersion

SharePoint 2010 - 客户端对象模型 - 向ListItem添加附件

检查列表列是否存在使用SharePoint客户端对象模型?

SharePoint Server 对象模型(SPSite 和 SPWeb)