从SharePoint下载文件时出现(401)未经授权的异常

9
我使用OAuth机制为SharePoint Online服务器生成了访问令牌。我使用这个令牌使用CSOM创建ClientContext。虽然我能够无缝访问所有站点、库和文件夹,但是在从SharePoint Online下载文件时出现错误。

远程服务器返回错误:(401) 未经授权。

以下是我用于文件下载的代码:

var clientContext = TokenHelper.GetClientContextWithAccessToken("https://adventurer.sharepoint.com/Subsite1", accessToken);
var list = clientContext.Web.Lists.GetByTitle("SubSite 1 Library 1");
string vquery = @"<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='UniqueId' /><Value Type='Lookup'>" + "6718053d-a785-489c-877f-5a4b88dcb2a7" + "</Value></Eq></Where></Query></View>";
CamlQuery query = new CamlQuery();
query.ViewXml = vquery;
var listItems = list.GetItems(query);
clientContext.Load(listItems, items => items.Take(1).Include(item => item.File));
clientContext.ExecuteQuery();

var fileRef = listItems[0].File.ServerRelativeUrl;
var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(clientContext, fileRef);

我不理解这个错误的根本原因,因为我正在使用正确的访问令牌传递客户端上下文。我想知道OpenBinaryDirect是否有与访问令牌一起使用的限制? 如果没有,那么上面的代码有什么问题?是否有其他替代方法可以使用访问令牌进行下载?

2个回答

14

经过尝试很多替代方法,我得出结论:OpenBinaryDirect()不能与OAuth令牌一起使用。我能够使用另外两种方法从SharePoint Online下载文件。我在这里发布答案以便帮助其他人:

方法1(OpenBinaryStream):

var file = clientContext.Web.GetFileByServerRelativeUrl(fileRef);
clientContext.Load(file);
clientContext.ExecuteQuery();                    
ClientResult<Stream> streamResult = file.OpenBinaryStream();
clientContext.ExecuteQuery();

虽然这种方法可以完美地运作,OpenBinaryStreamMicrosoft.SharePoint.Client.dll <= v 14.0.0.0 中不可用。

方法2(WebClient或其他Http请求):

string downloadUrl = HostURL + "/_api/web/getfilebyserverrelativeurl('" + fileRef + "')/$value";
WebClient client = new WebClient(); 
client.Headers.Add("Authorization", "Bearer " + accessToken);
client.DownloadFile(downloadUrl, filePath);

请注意我在WebClient中使用的下载URL。普通文件URL无法从SharePoint Online下载文件。


0
我正在使用C#,以下是我目前从SharePoint Online检索文档的方法。我在网格视图中向用户显示他们文档的列表,因此我使用文档填充一个DataTable。如果您能够像我一样使用服务帐户而不是访问令牌,则可以尝试这种方法。
名称空间
using Microsoft.SharePoint.Client;
using SP = Microsoft.SharePoint.Client;

对象属性

SecureString securePassword = new SecureString();
private string username = "";    
ClientContext context = new SP.ClientContext("https://<root>.sharepoint.com/<site collection (unless root)>/<site>");

构造函数(这是我进行身份验证的方式)

public SharePoint()
{
    securePassword = convertToSecureString(System.Web.Configuration.WebConfigurationManager.AppSettings["O365PW"]);
    username = System.Web.Configuration.WebConfigurationManager.AppSettings["O365UN"];
    context.Credentials = new SharePointOnlineCredentials(username, securePassword);
}

获取文档的方法

public DataTable GetDocuments(int changeID)
{
    DataTable dt = new DataTable("ChangeDocuments");
    DataRow dr = dt.NewRow();
    dt.Columns.Add("Title");
    dt.Columns.Add("URL");
    dt.Columns.Add("ChangeID");
    dt.Columns.Add("Modified");
    dt.Columns.Add("ID");


    // The SharePoint web at the URL.
    Web web = context.Web;

    // We want to retrieve the web's properties.
    context.Load(web);

    // We must call ExecuteQuery before enumerate list.Fields. 
    context.ExecuteQuery();

   // Assume the web has a list named "Announcements". 
   SP.List oList = context.Web.Lists.GetByTitle("Name of your document library");

   // This creates a CamlQuery that has a RowLimit of 100, and also specifies Scope="RecursiveAll" 
   // so that it grabs all list items, regardless of the folder they are in. 
   CamlQuery query = CamlQuery.CreateAllItemsQuery(100);
query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='ChangeID'/>" +
    "<Value Type='Number'>" + changeID + "</Value></Eq></Where></Query><RowLimit>100</RowLimit></View>";
   SP.ListItemCollection items = oList.GetItems(query);

   // Retrieve all items in the ListItemCollection from List.GetItems(Query). 
   context.Load(items);
   context.ExecuteQuery();

foreach (Microsoft.SharePoint.Client.ListItem listItem in items)
{
    // We have all the list item data. For example, Title. 

    dr = dt.NewRow();

    dr["Title"] = listItem["FileLeafRef"];

    if (String.IsNullOrEmpty(listItem["ServerRedirectedEmbedUrl"].ToString()))
    {
        dr["URL"] = "<root>/<site>/<document library>" + listItem["FileLeafRef"].ToString();
    }
    else
    {
        dr["URL"] = listItem["ServerRedirectedEmbedUrl"];
    }

    dr["ChangeID"] = listItem["ChangeID"];
    dr["Modified"] = Convert.ToDateTime(listItem["Modified"]).ToString("MMM.dd,yyyy h:mm tt");
    dr["ID"] = listItem["ID"];
    dt.Rows.Add(dr);
 }

return dt;
}

方法将密码转换为安全字符串
private SecureString convertToSecureString(string strPassword)
{
        var secureStr = new SecureString();
        if (strPassword.Length > 0)
        {
            foreach (var c in strPassword.ToCharArray()) secureStr.AppendChar(c);
        }
        return secureStr;
}

1
我正在使用OAuth机制来对SharePoint Online的用户进行身份验证。因此,我正在生成带有访问令牌的客户端上下文。以下代码不能用于此目的:context.Credentials = new SharePointOnlineCredentials(username, securePassword); - Saket Kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接