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

使用Azure应用凭据上传文件到SharePoint的问题及报错咨询

问题描述

我已注册拥有SharePoint站点完全控制权限的Azure应用,持有SharePoint Site Url、TenantId、ClientId、ClientSecret这些变量,需要上传文档到SharePoint站点文件夹。

  • 尝试使用PnP Core SDK时遇到认证问题:该SDK没有支持纯密码的认证提供者(UsernamePasswordAuthenticationProvider不接受应用名称作为用户名),且依赖Microsoft.Extensions.Hosting.Host给控制台应用增加了复杂度,想了解是否可通过PnP完成认证,或是否应直接使用REST API。
  • 了解到即将弃用的PnP Framework可对Azure应用进行认证,但资料有限,尝试以下代码后抛出异常:
using Microsoft.SharePoint.Client;
using PnP.Core.Model.SharePoint;
using PnP.Framework;

ClientContext context =
                new AuthenticationManager()
                    .GetACSAppOnlyContext(
                        siteUrl: "siteUrl",
                        appId: "clientId",
                        appSecret: "password");
    
IFolder? folder = (IFolder?)context.Web.Folders.Where(f => f.Name == directory).FirstOrDefault();

if (folder == null) throw new Exception("Folder not found.");

folder.Files.Add(filename, content, overwrite);

抛出异常:

Microsoft.SharePoint.Client.CollectionNotInitializedException: 'The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.'

请问如何显式请求该集合?

解决方案

一、修复PnP Framework的代码问题

你遇到的CollectionNotInitializedException是因为SharePoint Client Object Model(CSOM)的延迟加载机制:context.Web.Folders是未加载的集合,必须显式触发请求才能获取数据。修改代码如下:

using Microsoft.SharePoint.Client;
using PnP.Framework;
using System.IO;

// 初始化ACS应用权限上下文,用using管理生命周期
using var context = new AuthenticationManager().GetACSAppOnlyContext(
    siteUrl: "你的站点URL",
    appId: "你的ClientId",
    appSecret: "你的ClientSecret");

// 显式加载Web的Folders集合,仅指定需要的Name字段以减少数据传输
context.Load(context.Web.Folders, folders => folders.Include(f => f.Name));
// 执行请求到SharePoint服务器,获取集合数据
context.ExecuteQuery();

// 查找目标文件夹
var folder = context.Web.Folders.FirstOrDefault(f => f.Name == "目标文件夹名称");
if (folder == null) throw new Exception("Folder not found.");

// 上传文件,Add方法需配合ExecuteQuery完成请求
folder.Files.Add("文件名.txt", File.ReadAllBytes("本地文件路径"), overwrite: true);
context.ExecuteQuery();

关键修复点:

  • 用context.Load()指定要加载的集合及字段,避免冗余数据
  • 调用context.ExecuteQuery()触发实际的HTTP请求,完成集合初始化
  • 用using语句管理ClientContext,避免资源泄漏

二、PnP Core SDK的简化认证方案

虽然PnP Core SDK依赖依赖注入,但控制台应用可极简配置,无需复杂主机设置。针对Azure应用权限(ClientSecret),应使用ClientSecretAuthenticationProvider而非用户密码类型的UsernamePasswordAuthenticationProvider:

using PnP.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using System.IO;

// 配置基础服务
var services = new ServiceCollection();
services.AddPnPCore(options =>
{
    options.DefaultAuthenticationProvider = new ClientSecretAuthenticationProvider(
        "你的TenantId",
        "你的ClientId",
        "你的ClientSecret");
});

// 初始化服务容器
using var serviceProvider = services.BuildServiceProvider();
var pnpContextFactory = serviceProvider.GetRequiredService<IPnPContextFactory>();

// 获取站点上下文
using var context = await pnpContextFactory.CreateAsync("你的站点URL");

// 查找目标文件夹并上传文件
var folder = await context.Web.Folders.FirstOrDefaultAsync(f => f.Name == "目标文件夹名称");
if (folder == null) throw new Exception("Folder not found.");

await folder.Files.AddAsync("文件名.txt", File.ReadAllBytes("本地文件路径"), overwrite: true);

三、REST API备选方案

若不想使用PnP库,可直接调用SharePoint REST API完成上传:

  1. 获取Azure AD访问令牌:
using Microsoft.Identity.Client;

var app = ConfidentialClientApplicationBuilder
    .Create("你的ClientId")
    .WithClientSecret("你的ClientSecret")
    .WithAuthority($"https://login.microsoftonline.com/你的TenantId")
    .Build();

var scopes = new[] { "https://{你的租户域名}.sharepoint.com/.default" };
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
var accessToken = result.AccessToken;
  1. 调用REST API上传文件:
using System.Net.Http;
using System.Net.Http.Headers;
using System.IO;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

// 替换为你的站点及文件夹路径
var requestUrl = $"https://{你的租户域名}.sharepoint.com/sites/你的站点名/_api/web/folders/getbyurl('Documents/目标文件夹')/files/add(overwrite=true,url='文件名.txt')";

var content = new ByteArrayContent(File.ReadAllBytes("本地文件路径"));
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

var response = await client.PostAsync(requestUrl, content);
response.EnsureSuccessStatusCode();

内容的提问来源于stack exchange,提问作者Muflix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 08:30:47