如何在不使用REST API的情况下用C#从SharePoint Online下载文件?
我尝试仅通过SharePoint Online的共享文档链接下载文件,示例链接:https://ourcompanysite.sharepoint.com/:p:/s/CP/EUo3_Xs4SKtAsSZCZxJBXyQBAFRhL3pXz7-sM_F-RTyzcA?e=4%3a85JGQ2&at=9
使用System.Net.WebClient实现的代码如下:
using (WebClient loWC = new System.Net.WebClient()) { string lsFileName = "https://companysite.sharepoint.com/:p:/s/CP/EUo3_Xs4SKzcA?e=4%3a85JGQ2&at=9"; Uri loURI = new Uri(lsFileName); loWC.Credentials = new NetworkCredential("myuser@company.com.au", "Pass123"); byte[] loData = null; ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; ServicePointManager.DefaultConnectionLimit = 9999; byte[] loData = loWC.DownloadData(loURI); }
执行loWC.DownloadData时触发403 Forbidden错误,但我确实拥有该站点的访问权限。
我的疑问:
- 当用户名是邮箱格式(无单独域/用户名)时,这种凭证传递方式是否可行?
- 是否可以不依赖SharePoint REST API,仅通过这种方式下载文件(或获取内容)?因为我不清楚文档库的完整路径和文件名。
问题解答
1. 邮箱格式用户名的凭证传递问题
直接用NetworkCredential传递邮箱和密码的方式不可行。SharePoint Online采用OAuth 2.0或Azure AD身份验证机制,传统的NetworkCredential是针对NTLM/Kerberos等Windows本地身份验证设计的,无法适配云环境的身份验证流程。即使输入正确的邮箱和密码,也会被SharePoint Online的身份验证系统拒绝,导致403错误。
2. 不使用REST API通过共享链接下载文件的方案
可以实现,但需要先将共享链接转换为直接下载链接,并使用适配云环境的身份验证方式,具体方案如下:
步骤1:转换共享链接为直接下载链接
SharePoint的权限共享链接(如:p:/格式)需要转换为指向文件内容的直接URL,转换规则:
- 将链接中的
:p:/替换为:u:/ - 在链接末尾添加
&download=1
示例链接转换后为:https://ourcompanysite.sharepoint.com/:u:/s/CP/EUo3_Xs4SKtAsSZCZxJBXyQBAFRhL3pXz7-sM_F-RTyzcA?e=4%3a85JGQ2&at=9&download=1
步骤2:使用正确的身份验证方式
放弃WebClient的NetworkCredential,改用以下两种主流方式处理身份验证并下载文件:
方案A:Microsoft Graph API(推荐)
通过Graph API的shares端点解析共享链接,直接获取文件内容:
using Microsoft.Graph; using Azure.Identity; var scopes = new[] { "https://graph.microsoft.com/.default" }; var tenantId = "你的租户ID"; var clientId = "你的应用注册ID"; var clientSecret = "你的应用密钥"; var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret); var graphClient = new GraphServiceClient(clientSecretCredential, scopes); // 编码共享链接 string encodedUrl = System.Web.HttpUtility.UrlEncode("https://ourcompanysite.sharepoint.com/:p:/s/CP/EUo3_Xs4SKtAsSZCZxJBXyQBAFRhL3pXz7-sM_F-RTyzcA?e=4%3a85JGQ2&at=9"); var sharedItem = await graphClient.Shares[$"u!{encodedUrl}"].Request().GetAsync(); // 获取文件内容流 var fileContentStream = await graphClient.Shares[$"u!{encodedUrl}"].DriveItem.Content.Request().GetAsync();
方案B:SharePoint CSOM
通过CSOM解析共享链接并下载文件,需先安装Microsoft.SharePointOnline.CSOM NuGet包:
using Microsoft.SharePoint.Client; using System.Security; using (var context = new ClientContext("https://ourcompanysite.sharepoint.com/sites/CP")) { // 用户名密码验证(注:该方式已逐步被弃用,优先推荐应用权限) context.Credentials = new SharePointOnlineCredentials("myuser@company.com.au", ConvertToSecureString("Pass123")); // 通过共享链接获取文件对象 var file = Web.GetFileByUrl(context, "https://ourcompanysite.sharepoint.com/:p:/s/CP/EUo3_Xs4SKtAsSZCZxJBXyQBAFRhL3pXz7-sM_F-RTyzcA?e=4%3a85JGQ2&at=9"); context.Load(file); context.ExecuteQuery(); // 下载文件内容 var fileContentStream = file.OpenBinaryStream(); context.ExecuteQuery(); } // 辅助方法:将字符串转为SecureString private static SecureString ConvertToSecureString(string password) { var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } return securePassword; }
注意事项
- 若没有应用权限(clientId/clientSecret),可临时使用用户名密码流,但该方式可能被租户禁用,长期来看优先采用应用权限。
- 即使转换了下载链接,仍需正确的身份验证才能访问,否则仍会返回403错误。
内容的提问来源于stack exchange,提问作者Programnik

