如何用C# Graph SDK及自定义GraphHandler上传文件/文件夹至SharePoint文档库
前置说明
- 修正原
GraphHandler类的拼写错误:构造函数参数clienSecret需改为clientSecret,否则会导致初始化失败。 - Azure应用需配置应用权限:添加
Sites.ReadWrite.All权限并完成管理员同意,否则调用Graph API会返回权限不足错误。
扩展GraphHandler类
在原类基础上添加文件/文件夹上传相关方法,完整代码如下:
using Azure.Identity; using Microsoft.Graph; using Microsoft.Graph.Models; using System.IO; using System.Threading.Tasks; namespace UploadFiles; public class GraphHandler { public GraphServiceClient GraphClient { get; set; } // 修正参数拼写错误 public GraphHandler(string tenantId, string clientId, string clientSecret) { GraphClient = CreateGraphClient(tenantId, clientId, clientSecret); } public GraphServiceClient CreateGraphClient(string tenantId, string clientId, string clientSecret) { var option = new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud }; var clientSecretCredentials = new ClientSecretCredential(tenantId, clientId, clientSecret, option); var scope = new[] { "https://graph.microsoft.com/.default" }; return new GraphServiceClient(clientSecretCredentials, scope); } #region 文档库操作核心方法 /// <summary> /// 根据站点名称和文档库名称获取对应的Drive对象 /// </summary> /// <param name="siteName">SharePoint站点名称(即站点URL末尾的名称)</param> /// <param name="libraryName">文档库显示名称</param> /// <returns>目标Drive</returns> public async Task<Drive> GetSiteDriveAsync(string siteName, string libraryName) { // 获取指定站点 var site = await GraphClient.Sites.GetByPath(siteName, "root").GetAsync(); // 获取站点下的文档库 var drives = await GraphClient.Sites[site.Id].Drives.GetAsync(); var targetDrive = drives.Value.FirstOrDefault(d => d.Name.Equals(libraryName)); if (targetDrive == null) throw new Exception($"未找到名称为{libraryName}的文档库"); return targetDrive; } /// <summary> /// 上传单个文件到文档库指定路径 /// </summary> /// <param name="localFilePath">本地文件完整路径</param> /// <param name="siteName">SharePoint站点名称</param> /// <param name="libraryName">文档库名称</param> /// <param name="remoteFolderPath">文档库内目标文件夹路径(如"Folder1/SubFolder",空则上传到根目录)</param> public async Task UploadSingleFileAsync(string localFilePath, string siteName, string libraryName, string remoteFolderPath = "") { var drive = await GetSiteDriveAsync(siteName, libraryName); var fileName = Path.GetFileName(localFilePath); // 拼接远程文件路径 var remoteFilePath = string.IsNullOrEmpty(remoteFolderPath) ? fileName : $"{remoteFolderPath}/{fileName}"; // 流式上传文件 using var fileStream = new FileStream(localFilePath, FileMode.Open); await GraphClient.Drives[drive.Id].Items["root:/"+remoteFilePath+":/"].Content.PutAsync(fileStream); } /// <summary> /// 递归上传本地文件夹到文档库 /// </summary> /// <param name="localFolderPath">本地文件夹完整路径</param> /// <param name="siteName">SharePoint站点名称</param> /// <param name="libraryName">文档库名称</param> /// <param name="remoteParentPath">文档库内父文件夹路径(空则上传到根目录)</param> public async Task UploadFolderAsync(string localFolderPath, string siteName, string libraryName, string remoteParentPath = "") { var drive = await GetSiteDriveAsync(siteName, libraryName); var folderName = Path.GetFileName(localFolderPath); // 创建远程文件夹 var remoteFolderPath = string.IsNullOrEmpty(remoteParentPath) ? folderName : $"{remoteParentPath}/{folderName}"; var newFolder = new DriveItem { Name = folderName, Folder = new Folder(), AdditionalData = new Dictionary<string, object> { {"@microsoft.graph.conflictBehavior", "rename"} // 冲突时自动重命名 } }; await GraphClient.Drives[drive.Id].Items["root:/"+remoteParentPath+":/"].Children.PostAsync(newFolder); // 上传文件夹内的所有文件 var files = Directory.GetFiles(localFolderPath); foreach (var file in files) { await UploadSingleFileAsync(file, siteName, libraryName, remoteFolderPath); } // 递归上传子文件夹 var subFolders = Directory.GetDirectories(localFolderPath); foreach (var subFolder in subFolders) { await UploadFolderAsync(subFolder, siteName, libraryName, remoteFolderPath); } } #endregion }
使用示例
class Program { static async Task Main(string[] args) { // 替换为你的Azure应用信息 string tenantId = "你的TenantId"; string clientId = "你的ClientId"; string clientSecret = "你的ClientSecret"; var graphHandler = new GraphHandler(tenantId, clientId, clientSecret); try { // 示例1:上传单个文件到文档库根目录 await graphHandler.UploadSingleFileAsync(@"C:\Files\test.docx", "你的站点名称", "文档库名称"); // 示例2:上传单个文件到文档库指定子文件夹 await graphHandler.UploadSingleFileAsync(@"C:\Files\report.xlsx", "你的站点名称", "文档库名称", "Reports/2024"); // 示例3:递归上传本地文件夹到文档库 await graphHandler.UploadFolderAsync(@"C:\Files\ProjectFiles", "你的站点名称", "文档库名称"); } catch (Exception ex) { Console.WriteLine($"上传失败:{ex.Message}"); } } }
关键说明
- 站点名称:指SharePoint站点URL末尾的标识,例如站点URL为
https://contoso.sharepoint.com/sites/Marketing,则站点名称为Marketing。 - 冲突处理:创建文件夹时设置了
@microsoft.graph.conflictBehavior为rename,避免同名文件夹冲突导致报错,可根据需求改为replace或fail。 - 大文件上传:如果需要上传超过4MB的文件,建议使用Graph SDK的分片上传功能,上述示例适用于小文件,大文件可扩展使用
LargeFileUploadTask实现。
内容的提问来源于stack exchange,提问作者Ashish Prajapati
相关产品推荐
相关产品推荐

