.NET 把文件上传到Sharepoint - Microsoft Graph API方式

1 - Microsoft Graph API 必备前置信息

  • SiteUrl
  • ClientId
  • ClientSecret
  • TargetFolder

SiteUrl 取值来源:

点击主页,地址栏的就是SiteUrl

ClientId 和 ClientSecret 取值来源:

bash 复制代码
1. 打开 https://portal.azure.com 并登录
2. 进入应用注册
- 搜索并点击 **"Azure Active Directory"** (或 **"Microsoft Entra ID"**)
- 左侧菜单选择 **"应用注册"** (App registrations)
3.创建新应用(如果还没有)
- 点击 **"新注册"** (New registration)
- 填写名称(如 "KBS-SharePoint-App")
- 选择支持的账户类型(通常选择"仅此组织目录中的账户")
- 点击 **"注册"**
4.获取 ClientId
- 在应用的 **"概述"** (Overview) 页面
- 复制 **"应用程序(客户端)ID"** (Application (client) ID)
- 这就是你的 `ClientId`
5.创建 ClientSecret
- 左侧菜单选择 **"证书和密码"** (Certificates & secrets)
- 点击 **"新客户端密码"** (New client secret)
- 添加描述(如 "KBS App Secret")
- 选择过期时间
- 点击 **"添加"**
- **⚠️ 立即复制"值"列的内容**,这就是 `ClientSecret`(离开页面后将无法再查看)

如下图:

TargetFolder 取值来源:

bash 复制代码
sharepoint进入目标文件夹,点击"复制链接" 如下:
https://域名/:f:/r/sites/站点/Shared%20Documents/AI_KnowledgeBase/ADO?csf=1&web=1&e=CfytE3

站点/ 后边  到 ADO 的 ? 号前边
即:/Shared%20Documents/AI_KnowledgeBase/ADO

规则就是:/Shared Docments/自定义目录
注:Shared%20Documents是Shared Docments 带空格转义后的字符串。

JSON配置实例:

bash 复制代码
{
  "SharePoint": {
    "SiteUrl": "https://域名/sites/站点名称",
    "ClientId": "f988xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
   "ClientSecret": "xxxxx~xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "TargetFolder": "/Shared Documents/AI_KnowledgeBase/ADO"
  }
}

2 - Microsoft Graph API 方式上传文件到SharePoint必须的API权限

问答:

配置好的样子:

参照步骤:

添加权限 -> Microsoft API -> Microsoft Graph

应用程序权限

在应用程序里边找到

✅ Sites.ReadWrite.All

✅ Files.ReadWrite.All

注:这样设置是完整访问,开发/测试环境 推荐。


生产环境推荐如下:

bash 复制代码
✅ Sites.Selected
使用 Sites.Selected 后,需要通过 PowerShell 或 Graph API 授予特定站点权限:

# 授予特定站点权限
Connect-MgGraph -Scopes "Sites.FullControl.All"

$appId = "f9885bb6-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$siteId = "<你的站点ID>"

# 授予写入权限
Invoke-MgGraphRequest -Method POST `
  -Uri "https://graph.microsoft.com/v1.0/sites/$siteId/permissions" `
  -Body @{
    roles = @("write")
    grantedToIdentities = @(
      @{
        application = @{
          id = $appId
          displayName = "YourAppName"
        }
      }
    )
  }

3 - 实例代码

3.1 项目结构

3.1.1 appsettings.json

3.1.2 SharePointConfig.cs

3.1.3 Program.cs

csharp 复制代码
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Azure.Identity;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions;

namespace ConsoleApp1
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            // 检查是否运行诊断模式
            if (args.Length > 0 && args[0].Equals("--diagnose", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            try
            {
                // 读取配置文件
                var configuration = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .Build();

                var config = configuration.GetSection("SharePoint").Get<SharePointConfig>();
                if (config == null)
                {
                    Console.WriteLine("无法读取 SharePoint 配置");
                    return;
                }

                // 要上传的文件路径
                string localFilePath = @"D:\TempExcel\1.txt";
                
                if (!File.Exists(localFilePath))
                {
                    Console.WriteLine($"文件不存在: {localFilePath}");
                    return;
                }

                Console.WriteLine("开始上传文件到 SharePoint...");
                Console.WriteLine($"本地文件: {localFilePath}");
                Console.WriteLine($"目标站点: {config.SiteUrl}");
                Console.WriteLine($"目标文件夹: {config.TargetFolder}");

                // 上传文件
                await UploadFileToSharePoint(config, localFilePath);

                Console.WriteLine("文件上传成功!");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"错误: {ex.Message}");
                Console.WriteLine($"详细信息: {ex}");
                Console.WriteLine("\n提示: 运行 'dotnet run --diagnose' 来诊断配置问题");
            }
        }

        static async Task UploadFileToSharePoint(SharePointConfig config, string filePath)
        {
            // 从 SiteUrl 中提取租户、站点路径
            var uri = new Uri(config.SiteUrl);
            var tenantName = uri.Host.Split('.')[0]; // 例如:xxx
            var sitePath = uri.AbsolutePath; // 例如:/sites/xxx

            // 确定租户 ID
            string tenantId;
            if (!string.IsNullOrEmpty(config.TenantId))
            {
                tenantId = config.TenantId;
                Console.WriteLine($"使用配置的租户 ID: {tenantId}");
            }
            else
            {
                tenantId = $"{tenantName}.onmicrosoft.com";
                Console.WriteLine($"使用推断的租户 ID: {tenantId}");
            }

            // 创建 ClientSecretCredential
            Console.WriteLine($"客户端 ID: {config.ClientId}");
            var credential = new ClientSecretCredential(
                tenantId: tenantId,
                clientId: config.ClientId,
                clientSecret: config.ClientSecret
            );

            // 创建 Graph 客户端
            var graphClient = new GraphServiceClient(credential, new[] { "https://graph.microsoft.com/.default" });

            // 获取站点 ID
            Console.WriteLine("正在获取站点信息...");
            Console.WriteLine($"站点标识符: {tenantName}.sharepoint.com:{sitePath}");
            
            var site = await graphClient.Sites[$"{tenantName}.sharepoint.com:{sitePath}"].GetAsync();
            
            if (site == null || site.Id == null)
            {
                throw new Exception("无法获取站点信息");
            }

            Console.WriteLine($"站点 ID: {site.Id}");

            // 获取驱动器
            var drive = await graphClient.Sites[site.Id].Drive.GetAsync();
            if (drive == null || drive.Id == null)
            {
                throw new Exception("无法获取驱动器信息");
            }

            Console.WriteLine($"驱动器 ID: {drive.Id}");

            // 获取文件名
            string fileName = Path.GetFileName(filePath);

            // 构建目标路径(移除开头的斜杠)
            string targetPath = config.TargetFolder.TrimStart('/');
            string fullPath = $"{targetPath}/{fileName}";
            
            // 读取文件内容
            using var fileStream = File.OpenRead(filePath);
            
            // 上传文件
            Console.WriteLine($"正在上传文件: {fileName}...");
            
            // 对于小文件(< 4MB),使用简单上传
            var fileInfo = new FileInfo(filePath);
            if (fileInfo.Length < 4 * 1024 * 1024) // 4MB
            {
                // 使用 PUT 方法上传小文件
                await graphClient.Drives[drive.Id]
                    .Items["root"]
                    .ItemWithPath(fullPath)
                    .Content
                    .PutAsync(fileStream);

                Console.WriteLine($"文件已上传到: {fullPath}");
            }
            else
            {
                // 对于大文件,使用分块上传
                var uploadSessionRequestBody = new Microsoft.Graph.Drives.Item.Items.Item.CreateUploadSession.CreateUploadSessionPostRequestBody
                {
                    Item = new DriveItemUploadableProperties
                    {
                        AdditionalData = new Dictionary<string, object>
                        {
                            { "@microsoft.graph.conflictBehavior", "replace" }
                        }
                    }
                };

                var uploadSession = await graphClient.Drives[drive.Id]
                    .Items["root"]
                    .ItemWithPath(fullPath)
                    .CreateUploadSession
                    .PostAsync(uploadSessionRequestBody);

                if (uploadSession?.UploadUrl == null)
                {
                    throw new Exception("无法创建上传会话");
                }

                // 使用上传会话上传大文件
                var maxSliceSize = 320 * 1024; // 320 KB - 每次上传的块大小
                var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, fileStream, maxSliceSize, graphClient.RequestAdapter);

                var uploadResult = await fileUploadTask.UploadAsync();
                
                if (uploadResult.UploadSucceeded)
                {
                    Console.WriteLine($"大文件已上传到: {fullPath}");
                }
                else
                {
                    throw new Exception("文件上传失败");
                }
            }
        }
    }
}

4-1 运行结果 - Success


相关推荐
码农三叔14 小时前
(1-2)人形机器人的发展历史、趋势与应用场景:未来趋势与行业需求
人工智能·microsoft·机器人
天使奇迹14 小时前
2026年数字人视频生成平台评测与分析
microsoft
喵叔哟18 小时前
66.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--自动记账
微服务·架构·.net
laplace01231 天前
第七章 构建自己的agent智能体框架
网络·人工智能·microsoft·agent
江沉晚呤时1 天前
从零实现 C# 插件系统:轻松扩展应用功能
java·开发语言·microsoft·c#
共绩算力1 天前
世界模型正在掀起AI新浪潮
人工智能·microsoft·共绩算力
故事不长丨2 天前
C#log4net详解:从入门到精通,配置、实战与框架对比
c#·.net·wpf·log4net·日志·winform·日志系统
cjp5602 天前
002.为C#动态链接库添加wpf窗体
microsoft·c#·wpf
小丁爱养花2 天前
Coze 资源
人工智能·microsoft·ai