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

.NET 7 WPF应用调用Microsoft Graph上传OneDrive时出现ODataError

解决WPF中使用Microsoft Graph API上传OneDrive文件的ODataError问题

可能的问题及修复步骤

1. 租户ID拼写错误

你的代码中tenantId变量值包含空格("Tena ntId"),这会导致认证请求的Authority地址无效,获取的令牌可能无法正确访问OneDrive。修复:替换为无空格的正确租户ID。

2. 自定义认证提供者冗余且存在兼容性风险

自定义CustomAuthenticationProvider手动处理请求头,不符合Microsoft Graph SDK v5+的官方推荐方式,容易出现权限传递错误。

修复:
移除自定义CustomAuthenticationProvider类,使用Azure.Core.AccessTokenCredential初始化GraphServiceClient,代码更简洁且可靠:

// 替换原有的graphClient初始化代码
var credential = new AccessTokenCredential(result.AccessToken);
graphClient = new GraphServiceClient(credential);

需要添加命名空间:using Azure.Core;

3. 冗余的Drive请求

你先调用graphClient.Me.Drive.GetAsync()获取Drive ID,再通过graphClient.Drives[myDrive.Id]访问,多了一次不必要的API请求。可以直接访问当前用户的Drive根目录。

修复:
将上传会话的请求代码简化为:

var uploadSession = await graphClient.Me.Drive.Root
    .ItemWithPath(Path.GetFileName(filePath))
    .CreateUploadSession
    .PostAsync(uploadSessionRequestBody);

4. 不完整的异常信息捕获

当前捕获ODataError时仅打印基础消息,无法获取错误代码和详细信息,难以定位具体问题(如权限不足、路径非法等)。

修复:
完善异常处理逻辑,打印完整错误信息:

catch (ODataError ex)
{
    var errorDetails = ex.Error.Details != null 
        ? string.Join("\n", ex.Error.Details.Select(d => $"{d.Code}: {d.Message}")) 
        : "无详细信息";
    MessageBox.Show($"上传失败:\n错误代码: {ex.Error.Code}\n消息: {ex.Error.Message}\n详细信息: {errorDetails}", 
        "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}

修正后的完整代码

using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Graph.Models.ODataErrors;
using Microsoft.Identity.Client;
using Azure.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private GraphServiceClient graphClient;
        private bool IsAuthenticated => graphClient != null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            await Authenticate();
        }

        private async Task Authenticate()
        {
            var clientId = "你的ClientId";
            var redirectUri = "https://localhost/";
            var scopes = new[] { "User.Read", "Files.ReadWrite.All", "offline_access" };
            var tenantId = "你的正确TenantId"; // 修复:替换为无空格的正确租户ID

            IPublicClientApplication app = PublicClientApplicationBuilder
                .Create(clientId)
                .WithRedirectUri(redirectUri)
                .WithTenantId(tenantId) // 提前设置租户ID,避免后续重复指定
                .Build();

            var accounts = await app.GetAccountsAsync();
            AuthenticationResult result;
            try
            {
                result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                    .ExecuteAsync();
            }
            catch (MsalUiRequiredException)
            {
                result = await app.AcquireTokenInteractive(scopes)
                    .WithPrompt(Prompt.SelectAccount)
                    .ExecuteAsync();
            }

            // 使用官方推荐的TokenCredential初始化GraphServiceClient
            var credential = new AccessTokenCredential(result.AccessToken);
            graphClient = new GraphServiceClient(credential);
        }

        private async void btnSelectFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            if (openFileDialog.ShowDialog() == true)
            {
                string filePath = openFileDialog.FileName;
                await UploadFile(filePath);
            }
        }

        private async Task UploadFile(string filePath)
        {
            if (!IsAuthenticated)
            {
                MessageBox.Show("请先完成认证。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!File.Exists(filePath))
            {
                MessageBox.Show("所选文件不存在。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            try
            {
                var uploadSessionRequestBody = new Drives.Item.Items.Item.CreateUploadSession.CreateUploadSessionPostRequestBody
                {
                    Item = new DriveItemUploadableProperties
                    {
                        AdditionalData = new Dictionary<string, object>
                        {
                            { "@microsoft.graph.conflictBehavior", "replace" },
                        },
                    },
                };

                // 简化Drive访问路径,直接使用当前用户的根目录
                var uploadSession = await graphClient.Me.Drive.Root
                    .ItemWithPath(Path.GetFileName(filePath))
                    .CreateUploadSession
                    .PostAsync(uploadSessionRequestBody);

                int maxSliceSize = 320 * 1024;
                using (var fileStream = File.OpenRead(filePath))
                {
                    var fileUploadTask = new LargeFileUploadTask<DriveItem>(
                        uploadSession, fileStream, maxSliceSize);

                    var totalLength = fileStream.Length;
                    IProgress<long> progress = new Progress<long>(prog =>
                    {
                        Console.WriteLine($"已上传 {prog} / {totalLength} 字节");
                    });

                    var uploadResult = await fileUploadTask.UploadAsync(progress);

                    if (uploadResult.UploadSucceeded)
                    {
                        MessageBox.Show($"上传完成,文件ID: {uploadResult.ItemResponse.Id}", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
                    }
                    else
                    {
                        MessageBox.Show("上传失败", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
            }
            catch (ODataError ex)
            {
                var errorDetails = ex.Error.Details != null 
                    ? string.Join("\n", ex.Error.Details.Select(d => $"{d.Code}: {d.Message}")) 
                    : "无详细信息";
                MessageBox.Show($"上传失败:\n错误代码: {ex.Error.Code}\n消息: {ex.Error.Message}\n详细信息: {errorDetails}", 
                    "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"上传出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}

额外检查项

  • 确保Azure AD应用注册中已添加Files.ReadWrite.All权限,并已授予管理员同意(如果是租户范围内的应用)。
  • 确认本地文件路径无特殊字符,OneDrive文件名符合命名规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 05:44:52