В одном из проектов появилась необходимость использования 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