2017-02-18 36 views
1

Я не могу найти никакой документации, которая описывает правильный способ использовать OneDrive для хранения и сохранить приложение файлы syncrhonised различных устройств в C#Правильный способ использования OneDrive API для синхронизации файлов

Я прочитал документацию на OneDrive Dev Center но Я не понимаю http-код. (самообученный C# только).

Я понимаю, что я использую метод delta для получения измененных файлов из OneDrive, а затем сохраняю локально, но я не могу точно определить, как это сделать, поэтому обойти его, проверив локальный vs OneDrive вручную, используя GetAsync<> методы. Моя текущая реализация (ниже для справки) кажется мне довольно неуклюжей по сравнению с тем, что, вероятно, лучше обрабатывается в API.

Кроме того, не существует функции обратного «дельта»? То есть, когда я пишу файл в приложение локально, тогда скажите 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 Следует иметь в виду, что один диск может хранить самую последнюю версию файла. Если вам нужно выполнить какую-либо синхронизацию (то есть объединить данные из локальной и одной версии диска), которые должны выполняться в вашем коде. –

ответ

2

Синхронизация требует несколько различных этапов, некоторые из которых OneDrive API поможет вам, некоторые из которых вы должны сделать сами.

Change Detection
Первый этап, очевидно, обнаружить, изменилось ли что-нибудь. 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. Изменения в иерархии могут быть обнаружены с помощью API delta:

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

    Это будет возвращать текущее состояние для всех элементов, которые изменились с момента последнего вызова delta. Если это первый вызов, previousDeltaToken будет null, и 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 ответа.Это независимый элемент, поэтому его нужно будет хранить в некотором глобальном поле.

Разрешение конфликтов
Если изменения были обнаружены с обеих сторон приложение нужно будет пройти через процесс разрешения конфликта. Приложение, которое не понимает синхронизируемые файлы, должно будет либо запросить пользователя для разрешения конфликтов вручную, либо сделать что-то вроде fork-файла, так что теперь есть две копии. Однако приложения, имеющие дело с пользовательскими форматами файлов, должны обладать достаточными знаниями для эффективного объединения файлов без какого-либо взаимодействия с пользователем. То, что влечет за собой, очевидно, полностью зависит от синхронизируемого файла.

Применить изменения
Заключительный шаг, чтобы подтолкнуть объединенное государство к везде, где требуется (например, если изменение было локальным задвиньте удаленное, если изменение было дистанционным задвиньте местное, в противном случае, если изменение было в оба места нажимают оба места). Важно убедиться, что этот шаг происходит таким образом, чтобы избежать замены содержимого, которое было написано после того, как шаг «Обнаружение изменений» произошел. Локально вы, вероятно, достигнете этого, заблокировав файл во время процесса, однако вы не можете сделать это с удаленным файлом. Вместо этого вы захотите использовать значение 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, поэтому я обновил это упоминание вместо« токена ». Это должно быть свойство в ответе от 'delta'. Вам нужно будет сохранить это, так как система знает последний раз, когда вы звонили, чтобы он мог вернуть вам только те вещи, которые изменились. – Brad