В одном из проектов появилась необходимость использования SkyDrive для бекапа пользовательских данных. Казалось бы, задача достаточно тривиальная. Но не тут то было.

Для того, чтобы использовать Live SDK необходимо его скачать и “зарегистрировать” свое приложение здесь.

При регистрации вы получите ключ, который указывается в атрибуте ClientId элемента SignInButton (специальная кнопка, скрывающая OAuth-авторизацию SkyDrive) из пространства

xmlns:sd="clr-namespace:Microsoft.Live.Controls;assembly=Microsoft.Live.Controls"

, которое входит в Live SDK.

Добавив в разметку следующий код - у вас будет кнопка для соединения со SkyDrive.

<sd:SignInButton 
  Name="signInButton" 
  Height="72" 
  VerticalAlignment="Top"
  Branding="Skydrive"
  ClientId="ЗДЕСЬ_ВАШ_CLIENTID"
  Content="Button"
  Scopes="wl.basic wl.photos wl.skydrive wl.offline_access wl.signin wl.skydrive_update"
  SessionChanged="signInButton_SessionChanged"
  TextType="SignIn" Grid.Row="5"/>

Подробно про scopes можете почитать на MSDN.

В обработчике события SessionChanged SignInButton необходимо из LiveConnectSessionChangedEventArgs сохранить сессию - она пригодится для создания клиента.

После всех проведенных манипуляций, у вас есть сессия пользователя (возвращается в LiveConnectSessionChangedEventArgs.Session события SignInButton.SessionChanged).

Сессия используется для создания LiveConnectClient - объекта для работы со SkyDrive.

LiveConnectClient содержит ряд методов для работы со SkyDrive - асинхронные загрузку и выгрузку данных из SkyDrive. Например, DownloadAsync, UploadAsync с событиями DownloadCompleted и UploadCompleted.

Реализовав бекап и восстановление этими методами столкнулся с проблемой - при FAS происходила ошибка, которая не позволяла приложению пройти сертификацию.

Разбираясь с ошибкой нашлось единственное решение - использовать другой метод у LiveConnectClient - BackgroundDownloadAsync и BackgroundUploadAsync :)

У данных методов есть нюансы:

  • данные из приложения в SkyDrive могут отправлять только из папки изолированного хранилища “\shared\transfers";
  • при повторном таком же запросе валится ошибка

Итогом для бэкапа в SkyDrive будет приблизительно такая функция:

void BackupToSkyDrive() {
  IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
  // переносим файл sourceName в папку \shared\transfers\.
  var fileToSave = new IsolatedStorageFileStream(sourceName, 
    FileMode.OpenOrCreate, 
    FileAccess.ReadWrite, 
    FileShare.None, 
    store);
  var sourceFile = store.OpenFile("\\shared\\transfers\\" + fileName, 
    FileMode.OpenOrCreate);
     
  fileToSave.CopyTo(sourceFile);
  fileToSave.Flush();
  sourceFile.Close();
  fileToSave.Close();

  // вся "магия" здесь :)
  try {
    LiveConnectClient client = new LiveConnectClient(session);
    // удаляем все запросы, чтоб не было ошибок дублей 
    // (добавьте Microsoft.Phone.BackgroundTransfer)
    if (BackgroundTransferService.Requests.Count() > 0) {
      foreach (var request in BackgroundTransferService.Requests) {
        BackgroundTransferService.Remove(request);
      }
    }
    // в колбэке можно, например, скрыть прогресс-бар.
    client.BackgroundUploadCompleted += 
      new EventHandler(client_BackgroundUploadCompleted);
    client.BackgroundUploadAsync("me/skydrive", 
      new Uri("/shared/transfers/" + fileName, 
      UriKind.RelativeOrAbsolute), 
      OverwriteOption.Overwrite);
        
  }
  catch (Exception ex) {
    MessageBox.Show(ex.Message);
  }
}

В получении содержимого файла (а не мета-информации файла) из SkyDrive есть особенность - необходимо знать id файла. Это вынуждает делать два запроса - для нахождения нужного файла методом GetAsync, после чего - получение содержимого файла:

void RestoreFromSkyDrive() {
  LiveConnectClient client = new LiveConnectClient(session);
  string id = string.Empty;
  
  // колбэк получения файлов
  client.GetCompleted += (obj, args) => {
    try {
      List items;
      // в args.Result["data"] лежат данные о файлах
      items = args.Result["data"] as List;
      // переберем все файлы
      foreach (object item in items) {
        Dictionary file = item as Dictionary;
        // если нашли наш файл (с искомым именем)
        if (file["name"].ToString() == fileName) {
          // то запомним его id
          id = file["id"].ToString();
          // удалим все активные запросы, чтоб избежать ошибок 
          // (добавьте Microsoft.Phone.BackgroundTransfer)
          if (BackgroundTransferService.Requests.Count() > 0) {
            foreach (var request in BackgroundTransferService.Requests) {
              BackgroundTransferService.Remove(request);
            }
          }

          // в колбэке, например, скроем прогресс-бар
          client.BackgroundDownloadCompleted += 
            new EventHandler(client_BackgroundDownloadCompleted);
          // отправим запрос на получение содержимого файла
          client.BackgroundDownloadAsync(String.Format("{0}/content", id), 
            new Uri("\\shared\\transfers\\" + fileName, 
            UriKind.RelativeOrAbsolute));
        }
      }
    }
    catch (Exception ex) {
      MessageBox.Show(ex.Message);
    }
  };
  
  if (client != null) {
    client.GetAsync("me/skydrive/files");
  }
}
// в колбэке перенесем файл обратно, где лежал начальный файл
void client_BackgroundDownloadCompleted(object sender, LiveOperationCompletedEventArgs e) {
      if (e.Error == null) {
        var stream = e.Result;

        using (IsolatedStorageFile storage = 
          IsolatedStorageFile.GetUserStoreForApplication()) {
          var fileToSave = new IsolatedStorageFileStream(fileName, 
            FileMode.OpenOrCreate, 
            FileAccess.ReadWrite, 
            FileShare.None, 
            storage);
          var sourceFile = storage.FileExists("\\shared\\transfers\\" + fileName) ?
            storage.OpenFile("\\shared\\transfers\\" + fileName, FileMode.Open) : 
            null;

          if (sourceFile == null) {
            MessageBox.Show("not found");
          }
          else {
            sourceFile.CopyTo(fileToSave);
            sourceFile.Flush();
          }

          sourceFile.Close();
          fileToSave.Close();
        }
      }
      else {
        MessageBox.Show(e.Error.Message);
      }
      MessageBox.Show("restoring complited");
    }

Код выложен на GitHib