以编程方式从 MS 团队下载文件

Downloading file from MS teams programatically

之前,我开发了一个应用程序,它从公司的 Sharepoint 站点下载一个文件,然后用它执行一些魔术。

自从迁移到 MS Teams 后,我的权力就转移到了 MS Teams 上,我正在尝试更新应用程序以使用新平台。但是,我在下载文件时遇到了各种问题。

我的旧代码(为 Sharepoint 工作)使用 WebClient 根据用户先前提供的凭据检索文件:

    private string GetSchedule(string username, string password, string domain)
    {

        string tempPath = Path.GetTempFileName().Replace(".tmp", ".xlsm");
        using (WebClient client = new WebClient())
        {
            client.Credentials = new NetworkCredential(username, password, domain);
            try
            {
                client.DownloadFile(_networkSchedulePath, tempPath);
            }
            catch (WebException e)
            {
                if (e.Message.Contains("401"))
                {
                    StatusUpdated?.Invoke(this, new EventArgs<string>("Invalid Credentials Provided"));
                    Finished?.Invoke(this, null);
                    return null;
                }
                if (e.Message.Contains("404"))
                {
                    StatusUpdated?.Invoke(this, new EventArgs<string>("File Not Found"));
                    Finished?.Invoke(this, null);
                    return null;
                }
                else
                {
                    StatusUpdated?.Invoke(this, new EventArgs<string>(e.Message));
                    Finished?.Invoke(this, null);
                    return null;
                }
            }
        }
        return tempPath;
    }

但是,当我在新团队 link 中使用它时,我遇到了 403 Forbidden 错误。那么有没有办法以编程方式从 MS Teams 检索文件?

我看错了。简单地将 NetworkCredentials 替换为 SharePointOnlineCredentials 并不是解决方案。

我不确定以下是否是 "right" 方法,但它确实有效并且看起来很可靠。请试一试:

private static string GetFile(string path, string username, string password, string domain)
{
    var secureString = new SecureString();
    foreach (var ch in password)
    {
        secureString.AppendChar(ch);
    }

    string tempPath = Path.GetTempFileName().Replace(".tmp", ".xlsm");
    using (WebClient client = new WebClient())
    {
        var credentials = new SharePointOnlineCredentials(username, secureString);

        client.Headers[HttpRequestHeader.Cookie] = credentials.GetAuthenticationCookie(new Uri(path));
        try
        {
            client.DownloadFile(path, tempPath);
        }
        catch (WebException e)
        {
            // Error Handling
        }
    }
    return tempPath;
}

另一种选择是使用 CSOM 而不是直接使用网络客户端。 n.b., 我在使用 Microsoft.SharePoint.Client NuGet 包时在 OpenBinaryDirect() 调用时遇到错误,看起来这个包已经过时了。看来现在要用的是Microsoft.SharePointOnline.CSOM或者Microsoft.SharePoint2019.CSOM:

private static string GetFileWithClientContext(string path, string username, string password, string domain)
{
    var secureString = new SecureString();
    foreach (var ch in password)
    {
        secureString.AppendChar(ch);
    }
    string tempPath = Path.GetTempFileName().Replace(".tmp", Path.GetExtension(path));
    using (var context = new ClientContext(path))
    {
        context.Credentials = new SharePointOnlineCredentials(username, secureString);

        try
        {
            using (var file = Microsoft.SharePoint.Client.File.OpenBinaryDirect(context, new Uri(path).AbsolutePath))
            using (var outFile = System.IO.File.OpenWrite(tempPath))
            {
                file.Stream.CopyTo(outFile);
            }
        }
        catch (WebException e)
        {
            // Error Handling
        }
    }
    return tempPath;
}

感谢 JLRishe 提供的帮助和回答。但是,最终的解决方案与他的回答不同,这就是我将其发布在这里的原因:


OfficeDevPnP.Core 包被广泛用于此。

首先,AuthenticationManager 用于根据需要访问的特定共享点站点获取 ClientContext。这会弹出一个 window 以允许 MFA。然后通过 ClientContext 对象加载各种组件。从这里,文件通过 Guid 获取并转储到磁盘。

private string GetSchedule()
{
    string tempPath = Path.GetTempFileName().Replace(".tmp", ".xlsm");
    try
    {
        AuthenticationManager authManager = new OfficeDevPnP.Core.AuthenticationManager();
        ClientContext ctx = authManager.GetWebLoginClientContext("https://oursite.sharepoint.com/sites/ourspecificsite/");
        Web web = ctx.Web;
        Microsoft.SharePoint.Client.File schedule = web.GetFileById(new Guid("ourguid"));
        ctx.Load(web);
        ctx.Load(schedule);
        ctx.ExecuteQuery();
        FileInformation fInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctx, schedule.ServerRelativeUrl);
        using (var fileStream = File.Create(tempPath))
        {
            fInfo.Stream.CopyTo(fileStream);
        }
    }
    catch (WebException e)
    {
        StatusUpdated?.Invoke(this, new EventArgs<string>(e.Message));
        return null;
    }

    return tempPath;
}
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System.IO;
using System.Linq;

namespace Answer
{
    class Answer
    {
        static void Main(string[] args)
        {
            // Create Confidential Application
            IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
                .Create("<My_Azure_Application_Client_ID>")
                .WithTenantId("<My_Azure_Tenant_ID>")
                .WithClientSecret("<My_Azure_Application_Client_Secret>")
                .Build();

            // Create an authentication provider.
            ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
            // Configure GraphServiceClient with provider.
            GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);

            // Get a user
            var user = graphServiceClient.Users["<My_Azure_User_Name>"].Request().GetAsync().Result;
            
            // Get the teams the user is member of
            var joinedTeams = graphServiceClient.Users[user.Id].JoinedTeams.Request().GetAsync().Result;

            // Get the team we are intereseted in
            var team1 = joinedTeams.FirstOrDefault(t => t.DisplayName == "<TeamName_Of_Interest>");

            // Get the main folders
            var folders = graphServiceClient.Groups[team1.Id].Drive.Root.Children
                .Request()
                .GetAsync().Result;

            // Get the files in the first main folder
            var files = graphServiceClient.Groups[team1.Id].Drive.Items[folders[0].Id].Children
                .Request()
                .GetAsync().Result;

            // Get the file-Data of the first file
            MemoryStream fileData = graphServiceClient.Groups[team1.Id].Drive.Items[files[0].Id].Content
                .Request()
                .GetAsync().Result as MemoryStream;

            // Save the file to the hard-disc
            System.IO.File.WriteAllBytes($"C:\{files[0].Name}", fileData.ToArray());
        }
    }
}