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