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

如何用C# Graph API在SharePoint文档库创建并上传含文件的文件夹

基于GraphServiceClient实现SharePoint文档库操作

权限要求

确保你的应用已申请以下权限之一(根据实际场景选择,Delegated权限适合交互式场景,Application权限适合后台服务):

  • Files.ReadWrite.All
  • Sites.ReadWrite.All

一、创建单个文件夹

实现代码

假设你已掌握目标SharePoint站点ID和文档库ID(若未知文档库ID,可通过站点路径先获取对应Drive对象),以下是创建文件夹的具体实现:

using Microsoft.Graph;
using Microsoft.Graph.Models;

public class SharePointOperations
{
    private readonly GraphServiceClient _graphClient;

    // 注入已初始化的GraphServiceClient
    public SharePointOperations(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }

    /// <summary>
    /// 在SharePoint文档库指定路径下创建单个文件夹
    /// </summary>
    /// <param name="siteId">SharePoint站点ID</param>
    /// <param name="documentLibraryId">文档库对应的Drive ID</param>
    /// <param name="parentFolderPath">父文件夹路径,根路径可传""或"/"</param>
    /// <param name="folderName">目标文件夹名称</param>
    /// <returns>创建完成的DriveItem对象</returns>
    public async Task<DriveItem> CreateSingleFolderAsync(string siteId, string documentLibraryId, string parentFolderPath, string folderName)
    {
        var newFolder = new DriveItem
        {
            Name = folderName,
            Folder = new Folder(),
            AdditionalData = new Dictionary<string, object>
            {
                {"@microsoft.graph.conflictBehavior", "rename"} // 冲突时自动重命名,可选值:fail/rename/replace
            }
        };

        // 拼接父文件夹的请求路径
        var parentItemPath = string.IsNullOrEmpty(parentFolderPath.Trim()) 
            ? "root" 
            : $"root:/{parentFolderPath.Trim('/')}";
        
        return await _graphClient.Sites[siteId].Drives[documentLibraryId].Items[parentItemPath]
            .Children
            .Request()
            .AddAsync(newFolder);
    }
}

关键说明

  • @microsoft.graph.conflictBehavior 用于处理文件夹已存在的场景:
    • fail:默认值,直接抛出异常
    • rename:自动重命名为"FolderName 1"这类格式
    • replace:覆盖现有文件夹
  • 若不知道文档库ID,可通过站点域名+名称获取Drive列表:
    var drives = await _graphClient.Sites["{your-site-domain}:/sites/{your-site-name}"].Drives.Request().GetAsync();
    

二、上传本地文件夹至文档库

实现思路

  1. 递归遍历本地文件夹的所有文件和子文件夹
  2. 在文档库中同步创建对应的文件夹结构
  3. 逐个上传本地文件到对应文档库路径

实现代码

/// <summary>
/// 上传本地文件夹至SharePoint文档库指定路径
/// </summary>
/// <param name="siteId">SharePoint站点ID</param>
/// <param name="documentLibraryId">文档库Drive ID</param>
/// <param name="targetLibraryPath">文档库内的目标根路径,如"/Documents/UploadedFiles"</param>
/// <param name="localFolderPath">本地文件夹的绝对路径</param>
public async Task UploadLocalFolderAsync(string siteId, string documentLibraryId, string targetLibraryPath, string localFolderPath)
{
    if (!Directory.Exists(localFolderPath))
        throw new DirectoryNotFoundException($"本地文件夹不存在:{localFolderPath}");

    // 创建文档库中的目标根文件夹
    var targetRootFolder = await CreateSingleFolderAsync(siteId, documentLibraryId, 
        Path.GetDirectoryName(targetLibraryPath.Trim('/')), 
        Path.GetFileName(targetLibraryPath.Trim('/')));

    // 递归上传文件夹内容
    await UploadFolderRecursiveAsync(siteId, documentLibraryId, targetRootFolder.Id, localFolderPath);
}

/// <summary>
/// 递归处理文件夹内的文件和子文件夹
/// </summary>
private async Task UploadFolderRecursiveAsync(string siteId, string documentLibraryId, string parentFolderId, string localFolderPath)
{
    // 上传当前文件夹下的所有文件
    foreach (var filePath in Directory.GetFiles(localFolderPath))
    {
        var fileName = Path.GetFileName(filePath);
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        
        // 上传文件到指定父文件夹
        await _graphClient.Sites[siteId].Drives[documentLibraryId].Items[parentFolderId]
            .ItemWithPath(fileName)
            .Content
            .Request()
            .PutAsync<DriveItem>(fileStream);
    }

    // 处理当前文件夹下的所有子文件夹
    foreach (var subDirPath in Directory.GetDirectories(localFolderPath))
    {
        var subDirName = Path.GetFileName(subDirPath);
        
        // 在文档库父文件夹下创建子文件夹
        var subFolderItem = new DriveItem
        {
            Name = subDirName,
            Folder = new Folder()
        };
        var subFolder = await _graphClient.Sites[siteId].Drives[documentLibraryId].Items[parentFolderId]
            .Children
            .Request()
            .AddAsync(subFolderItem);
        
        // 递归上传子文件夹内容
        await UploadFolderRecursiveAsync(siteId, documentLibraryId, subFolder.Id, subDirPath);
    }
}

关键说明

  • 上传文件默认会覆盖同名文件,如需避免覆盖,可添加冲突处理头:
    await _graphClient.Sites[siteId].Drives[documentLibraryId].Items[parentFolderId]
        .ItemWithPath(fileName)
        .Content
        .Request()
        .Header("@microsoft.graph.conflictBehavior", "rename")
        .PutAsync<DriveItem>(fileStream);
    
  • 若文件超过4MB,建议使用分块上传(通过CreateUploadSession方法实现),上述代码适用于小文件场景。

使用示例

假设你的GraphHandler类提供了获取GraphServiceClient的方法,调用方式如下:

var graphClient = new GraphHandler().GetGraphClient();
var spOperations = new SharePointOperations(graphClient);

// 创建单个文件夹
await spOperations.CreateSingleFolderAsync(
    siteId: "your-site-id",
    documentLibraryId: "your-drive-id",
    parentFolderPath: "/Documents",
    folderName: "NewProjectFolder");

// 上传本地文件夹
await spOperations.UploadLocalFolderAsync(
    siteId: "your-site-id",
    documentLibraryId: "your-drive-id",
    targetLibraryPath: "/Documents/UploadedLocalFiles",
    localFolderPath: @"C:\LocalFiles\MyFolder");

内容的提问来源于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 07:20:32