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

10

我目前正在尝试实现几个任务,这些任务涉及从共享的 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
    }
}

我的问题始于"下载子项到路径"这一部分。我想要下载所有非文件夹的内容到一个临时文件中。但问题在于,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(但我认为这只会导致我获得共享文件夹的远程OneDrive 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

如果有人能指点我正确的方向,我将非常感激!谢谢!


乍一看你的代码似乎没问题。不过你可以尝试使用返回的文件@content.downloadUrl(它可能在AdditionalData字典中)。你需要创建一个HttpWebRequest(或类似的)来获取响应流,但这实际上会节省一次往返,因为/content在内部执行了重定向。 - Brad
真的起作用了!但这似乎不是正确的方法。我更喜欢使用Graph API来完成它。问题是,对于上传文件(这将是下一个任务),没有这样的快捷方式。无论如何,非常感谢你的帮助! - Romano Zumbé
1个回答

7
请求内容:
graphClient.Drives[remoteDriveId].Items[childItem.ParentReference.Id].Content.Request().GetAsync()

对应于获取DriveItem内容端点,仅在childItem.ParentReference.Id引用File资源时才有效,否则会出现预期的异常:

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

此示例演示了如何从“源”文件夹下载文件并将其上传到“目标”文件夹:

  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);

    }
 }

我认为你需要的不是下载/上传文件内容,而是使用DriveItem中的copymove操作。假设需要将文件从一个()文件夹复制到另一个(目标),则以下示例演示了如何完成此操作:

  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();
         }
      }
  }

我尝试了下载部分,但无法使其工作。我的第一行代码(var sharedItem = await graphClient.Drives[driveId].Items[folderItemId].Request().Expand(i => i.Children).GetAsync();)没有返回任何内容。也许我弄错了你使用的ID是什么。你能否使用我的代码示例中的变量?这将更容易理解。非常感谢! - Romano Zumbé
虽然复制或移动功能会是不错的补充(尤其是复制),但我也需要上传本地生成的文件。我已经为OneDrive的所有者实现了所有这些,但意识到对于使用共享文件夹的用户来说无法正常工作。 - Romano Zumbé
1
哦,天啊!多亏了你的示例,我终于把下载部分搞定了。那是父引用和项目 ID 的组合,我之前没试过!太棒了!现在只剩上传部分了……我会尝试更多的组合的 :-D - Romano Zumbé
1
我在赏金到期之前接受了此回答,因为它已经至少回答了我问题的50%。如果您还有上传的解决方案,那将非常棒,因为我还没有使其工作。 - Romano Zumbé
1
@RomanoZumbé,答案已更新,附有上传文件的示例(示例2)。 - Vadim Gremyachev
1
这太棒了!非常感谢你! - Romano Zumbé

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接