使用 MS Graph SDK 从共享的 OneDrive 文件夹下载和上传 DriveItem

Download and upload DriveItem from shared OneDrive Folder with MS Graph SDK

我目前正在尝试执行多项任务,包括从共享的 OneDrive 文件夹中列出、上传和下载文件。可通过登录用户 OneDrive 访问此文件夹(在其根文件夹中可见)。到目前为止,列表部分运行良好,使用此代码:

string remoteDriveId = string.Empty;
private GraphServiceClient graphClient { get; set; }
// Get the root of the owners OneDrive
DriveItem ownerRoot = await this.graphClient.Drive.Root.Request().Expand("thumbnails,children($expand=thumbnails)").GetAsync();
// Select the shared folders general information
DriveItem sharedFolder = ownerRoot.Children.Where(c => c.Name == "sharedFolder").FirstOrDefault();

// Check if it is a remote folder
if(sharedFolder.Remote != null)
{
    remoteDriveId = item.RemoteItem.ParentReference.DriveId;

    // Get complete Information of the shared folder
    sharedFolder = await graphClient.Drives[remoteDriveId].Items[sharedFolder.RemoteItem.Id].Request().Expand("thumbnails,children").GetAsync();
}

很明显,我需要从与其他 OneDrive 共享的 OneDrive 中检索共享文件夹信息。 我的下一部分是列出这个共享文件夹的内容,它也像这样工作得很好:

foreach (DriveItem child in sharedFolder.Children)
{
    DriveItem childItem = await graphClient.Drives[remoteDriveId].Items[child.Id].Request().Expand("thumbnails,children").GetAsync();

    if(childItem.Folder == null)
    {
         string path = Path.GetTempPath() + Guid.NewGuid();
         // Download child item to path
    }
}

我的问题从 "Download child item to path" 部分开始。在那里我想下载所有东西,那不是临时文件的文件夹。问题是 OneDrive 总是用一条错误消息来回答我的请求,即找不到文件。到目前为止我尝试的是:

using (var stream = await graphClient.Drives[remoteDriveId].Items[childItem.Id].Content.Request().GetAsync())
using (var outputStream = new System.IO.FileStream(path, System.IO.FileMode.Create))
{
    await stream.CopyToAsync(outputStream);
}

在另一个变体中,我尝试使用 childItem ParentReference 的 ID(但我认为这只会将我带到 sharedFolder 的远程 OneDrives ID):

using (var stream = await graphClient.Drives[remoteDriveId].Items[childItem.ParentReference.Id].Content.Request().GetAsync())
using (var outputStream = new System.IO.FileStream(path, System.IO.FileMode.Create))
{
    await stream.CopyToAsync(outputStream);
}

下载文件后,我想编辑它们并将它们重新上传到共享文件夹中的不同路径。该路径是由我创建的(已经可以使用),如下所示:

DriveItem folderToCreate = new DriveItem { Name = "folderName", Folder = new Folder() };
await graphClient.Drives[remoteDriveId].Items[sharedFolder.Id].Children.Request().AddAsync(folderToCreate);

上传失败。我试过这样:

using (var stream = new System.IO.FileStream(@"C:\temp\testfile.txt", System.IO.FileMode.Open))
{
    await graphClient.Drives[remoteDriveId].Items[sharedFolder.Id].Content.Request().PutAsync<DriveItem>(stream);
}

也喜欢这样(如果它不是共享文件夹,它会起作用,因此我使用 Drive 而不是 Drives):

using (var stream = new System.IO.FileStream(@"C:\temp\testfile.txt", System.IO.FileMode.Open))
{
    string folderPath = sharedFolder.ParentReference == null ? "" : sharedFolder.ParentReference.Path.Remove(0, 12) + "/" + Uri.EscapeUriString(sharedFolder.Name);
    var uploadPath = folderPath + "/" + uploadFileName;
    await graphClient.Drives[remoteDriveId].Root.ItemWithPath(uploadPath).Content.Request().PutAsync<DriveItem>(stream);
}

我无法使用 AddAsync 方法(就像在文件夹创建中一样),因为我不知道如何从 Stream 创建 DriveItem

如果有人能指出正确的方向,我将不胜感激!谢谢!

请求:

graphClient.Drives[remoteDriveId].Items[childItem.ParentReference.Id].Content.Request().GetAsync()

对应于Download the contents of a DriveItem endpoint and is only valid if childItem.ParentReference.Id refers to a File resource,在另一种情况下它失败并出现预期的异常:

Microsoft.Graph.ServiceException: Code: itemNotFound Message: You cannot get content for a folder

因此,要从文件夹下载内容,解决方案是:

  • 枚举文件夹下的项目:GET /drives/{drive-id}/items/{folderItem-id}/children
  • 如果 driveItem 对应于 File 方面,则每个项目明确下载其内容:GET /drives/{drive-id}/items/{fileItem-id}/content

例子

var sharedItem = await graphClient.Drives[driveId].Items[folderItemId].Request().Expand(i => i.Children).GetAsync();
foreach (var item in sharedItem.Children)
{
    if (item.File != null)
    {
        var fileContent = await graphClient.Drives[item.ParentReference.DriveId].Items[item.Id].Content.Request()
                    .GetAsync();
        using (var fileStream = new FileStream(item.Name, FileMode.Create, System.IO.FileAccess.Write))
           fileContent.CopyTo(fileStream);

    }
}

示例 2

该示例演示了如何从 source 文件夹下载文件并将其上传到 target 文件夹:

  var sourceDriveId = "--source drive id goes here--";
  var sourceItemFolderId = "--source folder id goes here--";
  var targetDriveId = "--target drive id goes here--";
  var targetItemFolderId = "--target folder id goes here--";

 var sourceFolder = await graphClient.Drives[sourceDriveId].Items[sourceItemFolderId].Request().Expand(i => i.Children).GetAsync();
 foreach (var item in sourceFolder.Children)
 {
    if (item.File != null)
    {
        //1. download a file as a stream
        var fileContent = await graphClient.Drives[item.ParentReference.DriveId].Items[item.Id].Content.Request()
            .GetAsync();
        //save it into file 
        //using (var fileStream = new FileStream(item.Name, FileMode.Create, System.IO.FileAccess.Write))
        //    fileContent.CopyTo(fileStream);


        //2.Upload file into target folder
        await graphClient.Drives[targetDriveId]
             .Items[targetItemFolderId]
             .ItemWithPath(item.Name)
             .Content
             .Request()
             .PutAsync<DriveItem>(fileContent);

    }
 }

而不是 downloading/uploading 文件内容,我认为你真正想要的是 DriveItem copy or move 操作。假设有一些文件需要从一个 (source) 文件夹 copied 到另一个 (target) 文件夹,那么下面的例子演示了如何完成它:

  var sourceDriveId = "--source drive id goes here--";
  var sourceItemFolderId = "--source folder id goes here--";
  var targetDriveId = "--target drive id goes here--";
  var targetItemFolderId = "--target folder id goes here--";

  var sourceFolder = await graphClient.Drives[sourceDriveId].Items[sourceItemFolderId].Request().Expand(i => i.Children).GetAsync();
  foreach (var item in sourceFolder.Children)
  {
      if (item.File != null)
      {
          var parentReference = new ItemReference
          {
               DriveId = targetDriveId,
               Id = targetItemFolderId
          };
          await graphClient.Drives[sourceDriveId].Items[item.Id]
              .Copy(item.Name, parentReference)
              .Request()
              .PostAsync();
         }
      }
  }