正确的使用方法OneDrive API来同步文件

问题描述:

我找不到概述了使用OneDrive来存储和保持在C#中跨设备syncrhonised应用程序文件的正确方式的任何文件正确的使用方法OneDrive API来同步文件

我在OneDrive Dev Center阅读文档,但我不明白的http代码。 (仅自学C#)。

我知道我使用delta方法从OneDrive中获取已更改的文件,然后在本地保存,但我无法弄清楚如何,所以通过使用GetAsync<>手动检查本地vs OneDrive方法。 我的当前实现(以下仅供参考)在我看来相当笨拙,而API可能处理得更好。

此外,它似乎没有反向'delta'功能?也就是说,我在本地向应用程序写入文件的位置,然后告诉OneDrive同步更改。那是因为我需要使用PutAsync<>方法实际上传它吗? (目前我在做什么)

public async Task<T> ReadFromXML<T>(string gamename, string filename) 
    { 
     string filepath = _appFolder + @"\" + gamename + @"\" + filename + ".xml"; 
     T objectFromXML = default(T); 
     var srializer = new XmlSerializer(typeof(T)); 
     Item oneDItem = null; 
     int casenum = 0; 
     //_userDrive is the IOneDriveClient 
     if (_userDrive != null && _userDrive.IsAuthenticated) 
     { 
      try 
      { 
       oneDItem = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync(); 
       if (oneDItem != null) casenum += 1; 
      } 
      catch (OneDriveException) 
      { } 
     } 
     StorageFile localfile = null; 
     try 
     { 
      localfile = await ApplicationData.Current.LocalFolder.GetFileAsync(filepath); 
      if (localfile != null) casenum += 2; 
     } 
     catch (FileNotFoundException) 
     { } 
     switch (casenum) 
     { 
      case 0: 
       //neither exist. Throws exception to tbe caught by the calling method, which should then instantiate a new object of type <T> 
       throw new FileNotFoundException(); 
      case 1: 
       //OneDrive only - should copy the stream to a new local file then return the object 
       StorageFile writefile = await ApplicationData.Current.LocalFolder.CreateFileAsync(filepath, CreationCollisionOption.ReplaceExisting); 
       using (var newlocalstream = await writefile.OpenStreamForWriteAsync()) 
       { 
        using (var oneDStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync()) 
        { 
         oneDStream.CopyTo(newlocalstream); 
        } 
       } 
       using (var newreadstream = await writefile.OpenStreamForReadAsync()) 
       { objectFromXML = (T)srializer.Deserialize(newreadstream); } 
       break; 
      case 2: 
       //Local only - returns the object 
       using (var existinglocalstream = await localfile.OpenStreamForReadAsync()) 
       { objectFromXML = (T)srializer.Deserialize(existinglocalstream); } 
       break; 
      case 3: 
       //Both - compares last modified. If OneDrive, replaces local data then returns the object 
       var localinfo = await localfile.GetBasicPropertiesAsync(); 
       var localtime = localinfo.DateModified; 
       var oneDtime = (DateTimeOffset)oneDItem.FileSystemInfo.LastModifiedDateTime; 
       switch (oneDtime > localtime) 
       { 
        case true: 
         using (var newlocalstream = await localfile.OpenStreamForWriteAsync()) 
         { 
          using (var oneDStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync()) 
          { oneDStream.CopyTo(newlocalstream); } 
         } 
         using (var newreadstream = await localfile.OpenStreamForReadAsync()) 
         { objectFromXML = (T)srializer.Deserialize(newreadstream); } 
         break; 
        case false: 
         using (var existinglocalstream = await localfile.OpenStreamForReadAsync()) 
         { objectFromXML = (T)srializer.Deserialize(existinglocalstream); } 
         break; 
       } 
       break; 
     } 
     return objectFromXML; 
    } 
+0

不确定您要做什么。 Windows 10有一个驱动器文件夹,您可以将其设置为在本地同步文件。 –

+0

据我所知,该文件夹不可用在电话设备上,只有桌面(可能还有平板电脑,但我没有一个可以验证)。尽管如此,我们的目标是将数据保存到本地设备,将文件同步到OneDrive,以便其他设备可以保持最新状态(并执行相同的操作)。数据文件太大而无法使用“RoamingData”。 – Lindsay

+2

如果您愿意,可以使用c#sdk。 https://github.com/onedrive/onedrive-sdk-csharp要记住的一件事是一个驱动器可以存储该文件的最新版本。如果您需要进行任何同步(即结合来自本地驱动器和一个驱动器版本的数据),则必须在您的代码中完成同步。 –

同步需要几个不同的步骤,其中一些OneDrive API将帮助您,其中一些你必须做你自己。

变化检测
第一阶段显然是检测是否有任何变化。该OneDrive API提供了两种机制,以检测服务的变化:

  1. 可以使用具有If-None-Match标准要求进行检测单个文件的更改:

    await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-None-Match", "etag") }).GetAsync(); 
    

    如果该文件不存在在所有你会得到一个404 Not Found。 否则,如果文件没有改变,你会得到一个304 Not Modified
    否则你会得到文件的当前状态。

  2. 的层次结构的变化可以使用delta API进行检测:

    await this.userDrive.Drive.Special.AppRoot.Delta(previousDeltaToken).Request().GetAsync(); 
    

    这将返回当前状态以来的delta上一次调用已更改的所有项目。如果这是第一次调用,则previousDeltaToken将为空,API将返回AppRoot内所有项目的当前状态。对于响应中的每个文件,您需要再次往返服务以获取内容。

在本地端,您需要枚举所有感兴趣的文件,并比较时间戳来确定,如果事情发生了变化。

很明显,前面的步骤需要了解“最后看到”状态,因此您的应用程序需要以某种形式的数据库/数据结构来跟踪这些情况。我建议跟踪以下内容:

+------------------+---------------------------------------------------------------------------+ 
|  Property  |         Why?         | 
+------------------+---------------------------------------------------------------------------+ 
| Local Path  | You'll need this so that you can map a local file to its service identity | 
| Remote Path  | You'll need this if you plan to address the remote file by path    | 
| Remote Id  | You'll need this if you plan to address the remote file by unique id   | 
| Hash    | The hash representing the current state of the file      | 
| Local Timestamp | Needed to detect local changes           | 
| Remote Timestamp | Needed for conflict resolution           | 
| Remote ETag  | Needed to detect remote changes           | 
+------------------+---------------------------------------------------------------------------+ 

此外,如果使用delta方法,你需要将token值从delta响应存储。这与项目无关,因此需要存储在某个全局字段中。

冲突解决
如果双方你的应用程序将需要经过一个解决冲突的过程中被检测到的变化。对同步文件缺乏了解的应用程序需要提示用户手动解决冲突,或者执行诸如分叉文件等操作,以便现在有两个副本。但是,处理自定义文件格式的应用程序应该有足够的知识来有效地合并文件,而无需任何形式的用户交互。这意味着完全依赖于正在同步的文件。

应用更改
的最后一步是推合并状态到任何需要(例如,如果变化本地然后推远程,如果改变被远程然后推本地,否则如果变化在两种地方推两地)。确保执行此步骤非常重要,以避免在“更改检测”步骤发生后替换写入的内容。在本地你可能通过在这个过程中锁定文件来完成这个任务,但是你不能用这个远程文件来完成。相反,您需要使用etag值来确保该服务只接受请求,如果状态仍然是您所期望的:

await this.userDrive.Drive.Special.AppRoot.ItemWithPath(remotePath).Content.Request(new Option[] { new HeaderOption("If-Match", "etag") }).PutAsync(newContentStream); 
+0

感谢您的详细回复,它看起来正是我需要处理的信息。我会在本周晚些时候出发,如果能够实现这个目标,我会给出答案。为了澄清'PreviousDeltaToken'的问题,应用程序需要在初始调用之后在本地存储哪些内容,以便在调用之间进行比较?如果是这样,那么你提到的DeltaLink值有什么不同? – Lindsay

+1

感谢您指出我对“deltaLink”的引用 - 它在您处理C#SDK时不适用,因此我已将该提及更新为'token'。它应该是来自“delta”调用的响应中的一个属性。你需要存储它,因为它是系统知道你最后一次呼叫的方式,以便它可以只返回那些已经改变的事情。 – Brad