2023年C#控制台程序上传文件至SharePoint文档库子文件夹方案问询
所需NuGet包
Microsoft.SharePointOnline.CSOM(版本16.0.24121.12000及以上,适配新版SharePoint Online)Microsoft.IdentityModel.Clients.ActiveDirectory(仅应用权限认证方案需要)
必要Using语句
using Microsoft.SharePoint.Client; using System.IO; using System.Security; using System.Linq; using Microsoft.IdentityModel.Clients.ActiveDirectory; // 仅方案2需要
方案1:基于用户名/密码认证(已禁用MFA场景)
适合已关闭MFA的环境,可使用用户密码或应用密码认证:
class Program { static void Main(string[] args) { // 配置参数 string siteUrl = "https://5mhhvs.sharepoint.com/sites/Documents"; string libraryName = "Documents"; // 目标文档库名称 string targetSubFolderPath = "年度报告/2024"; // 子文件夹路径(相对于文档库根目录) string localFilePath = @"C:\LocalFiles\2024年度报告.pdf"; // 认证信息 string userName = "your-account@5mhhvs.onmicrosoft.com"; string password = "your-password-or-app-password"; using (ClientContext context = new ClientContext(siteUrl)) { // 设置凭证 SecureString securePwd = new SecureString(); foreach (char c in password) securePwd.AppendChar(c); context.Credentials = new SharePointOnlineCredentials(userName, securePwd); try { // 获取文档库根文件夹 List library = context.Web.Lists.GetByTitle(libraryName); context.Load(library.RootFolder); context.ExecuteQuery(); // 创建或获取目标子文件夹 Folder targetFolder = GetOrCreateFolder(context, library.RootFolder.ServerRelativeUrl, targetSubFolderPath); // 上传文件(可设置是否覆盖现有文件) UploadFileToFolder(context, targetFolder, localFilePath, overwriteExisting: true); Console.WriteLine("文件上传成功"); } catch (Exception ex) { Console.WriteLine($"上传失败:{ex.Message}"); } } } // 递归创建/获取子文件夹 private static Folder GetOrCreateFolder(ClientContext context, string parentFolderUrl, string subFolderPath) { string[] folderSegments = subFolderPath.Split('/'); string currentFolderUrl = parentFolderUrl; foreach (string folderName in folderSegments) { FolderCollection folders = context.Web.GetFolderByServerRelativeUrl(currentFolderUrl).Folders; context.Load(folders); context.ExecuteQuery(); Folder targetFolder = folders.FirstOrDefault(f => f.Name.Equals(folderName, StringComparison.OrdinalIgnoreCase)); if (targetFolder == null) { targetFolder = context.Web.GetFolderByServerRelativeUrl(currentFolderUrl).Folders.Add(folderName); context.ExecuteQuery(); } currentFolderUrl = targetFolder.ServerRelativeUrl; } return context.Web.GetFolderByServerRelativeUrl(currentFolderUrl); } // 上传文件到指定文件夹 private static void UploadFileToFolder(ClientContext context, Folder targetFolder, string localFilePath, bool overwriteExisting) { string fileName = Path.GetFileName(localFilePath); byte[] fileBytes = File.ReadAllBytes(localFilePath); FileCreationInformation fileInfo = new FileCreationInformation { Content = fileBytes, Url = $"{targetFolder.ServerRelativeUrl}/{fileName}", Overwrite = overwriteExisting }; Microsoft.SharePoint.Client.File uploadedFile = targetFolder.Files.Add(fileInfo); context.Load(uploadedFile); context.ExecuteQuery(); } }
方案2:基于客户端ID/密钥认证(应用权限,推荐自动化任务)
适合长期自动执行的场景,无需依赖用户密码,需提前在Azure AD和SharePoint配置应用权限:
class Program { static void Main(string[] args) { // 配置参数 string siteUrl = "https://5mhhvs.sharepoint.com/sites/Documents"; string libraryName = "Documents"; string targetSubFolderPath = "年度报告/2024"; string localFilePath = @"C:\LocalFiles\2024年度报告.pdf"; // 应用认证信息 string clientId = "your-azure-ad-app-client-id"; string clientSecret = "your-azure-ad-app-client-secret"; string tenantId = "your-tenant-id"; using (ClientContext context = new ClientContext(siteUrl)) { // 设置应用凭证 context.AuthenticationMode = ClientAuthenticationMode.Default; context.FormDigestHandlingEnabled = false; context.Credentials = GetAppOnlyCredentials(siteUrl, clientId, clientSecret, tenantId); try { // 复用方案1的文件夹处理和文件上传逻辑 List library = context.Web.Lists.GetByTitle(libraryName); context.Load(library.RootFolder); context.ExecuteQuery(); Folder targetFolder = GetOrCreateFolder(context, library.RootFolder.ServerRelativeUrl, targetSubFolderPath); UploadFileToFolder(context, targetFolder, localFilePath, overwriteExisting: true); Console.WriteLine("文件上传成功"); } catch (Exception ex) { Console.WriteLine($"上传失败:{ex.Message}"); } } } // 获取应用权限凭证 private static SharePointOnlineCredentials GetAppOnlyCredentials(string siteUrl, string clientId, string clientSecret, string tenantId) { string resource = new Uri(siteUrl).Authority; string authority = $"https://login.microsoftonline.com/{tenantId}"; var authContext = new AuthenticationContext(authority); var clientCredential = new ClientCredential(clientId, clientSecret); var authResult = authContext.AcquireTokenAsync($"https://{resource}/", clientCredential).Result; return new SharePointOnlineCredentials(clientId, ConvertToSecureString(authResult.AccessToken)); } private static SecureString ConvertToSecureString(string input) { SecureString secureStr = new SecureString(); foreach (char c in input) secureStr.AppendChar(c); return secureStr; } // 复用方案1的GetOrCreateFolder和UploadFileToFolder方法 private static Folder GetOrCreateFolder(ClientContext context, string parentFolderUrl, string subFolderPath) { // 同方案1代码 } private static void UploadFileToFolder(ClientContext context, Folder targetFolder, string localFilePath, bool overwriteExisting) { // 同方案1代码 } }
问题解答
1. 若目标位置已存在文件,能否配置合并修改?
SharePoint无自动合并文件内容的原生功能,需根据文件类型自定义处理:
- Office文档(Word/Excel/PowerPoint):可通过Open XML SDK或Office Interop下载云端文件,对比本地文件内容差异后手动实现合并逻辑,再上传覆盖并保留版本历史(需开启文档库版本控制)。
- 非Office文件:仅支持覆盖旧文件(设置
Overwrite = true),或自定义对比逻辑合并内容后上传。
2. 若文件正被SharePoint用户打开编辑,能否强制上传?
文件被用户打开时会处于锁定状态,直接上传会触发锁定异常。可尝试以下方式强制上传,但存在数据丢失风险:
- 使用
File.SaveBinaryDirect()方法搭配覆盖参数,直接替换锁定文件; - 先调用
file.CheckOut()尝试解除锁定(若用户未独占编辑),再上传。
推荐优先检查文件锁定状态,若被锁定则记录日志并延后重试,避免破坏用户正在编辑的内容。
内容的提问来源于stack exchange,提问作者Dushan Rathnayake
相关产品推荐
相关产品推荐

