You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C# Graph SDK及自定义GraphHandler上传文件/文件夹至SharePoint文档库

基于GraphHandler实现SharePoint文档库文件/文件夹上传(C#)

前置说明

  1. 修正原GraphHandler类的拼写错误:构造函数参数clienSecret需改为clientSecret,否则会导致初始化失败。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:55:30