Работа с файлами на локальном ПК и в SkyDrive

Планшетные компьютеры, ставшие чрезвычайно популярными у пользователей, открыли новые возможности для мобильной работы и создания контента на ходу. Сегодня потребители хотят большего от своих планшетов: они стремятся иметь доступ к файлам с любого устройства, чтобы уменьшить объем занимаемого ими пространства в и без того небольшой по размеру памяти. В результате многие пользователи и разработчики перешли на облачные сервисы, которые предоставляют место для хранения файлов и одинаковый набор функций в любом месте и на любом устройстве.

Сервис SkyDrive — важнейший компонент операционной системы Windows 8.1, обеспечивающий непревзойденную гибкость в достижении этих целей. Пользователи могут с легкостью сохранять и открывать файлы SkyDrive прямо из приложений, просто выбирая файл или добавляя содержимое в свои библиотеки. Библиотеки  позволяют эффективно агрегировать файлы, записанные на локальном ПК и в SkyDrive, обеспечивая единый вход для доступа к контенту пользователя.

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

В данной статье я покажу, как быстро добавить в приложение поддержку SkyDrive, не усложняя при этом существующий код. Конкретнее, я остановлюсь на следующих моментах:

  1. Основы взаимодействия с пользовательскими файлами в Windows 8.1.
  2. Интеграция SkyDrive с ОС, модель данных WinRT.
  3. Обеспечение бесперебойной работы приложения в режиме онлайн и офлайн.
  4. Использование эскизов при выводе на экран изображений для достижения максимальной производительности.

О чем нужно помнить

Если вы читали мою статью о создании красивых представлений для локальных файлов или разрабатывали приложения для Windows 8, то, скорее всего, уже знакомы с методами и рекомендациями касательно доступа к файлам пользователей через WinRT API. Напомню, что получить эти файлы из приложения можно двумя способами: с помощью библиотек и средства выбора файлов.

  • Библиотеки используются приложениями, работающими в основном с коллекциями файлов (например, фотогалереями или музыкальными проигрывателями).
  • Средство выбора файлов (и отчасти расширение для активации файлов) служит для текущего взаимодействия с содержимым пользователя.
    • Начало передачи файла напоминает редактирование существующего файла.
    • Экспорт содержимого, созданного приложением, в файловое пространство пользователя.
      • Средство выбора файлов можно использовать для разового сохранения.
      • Средство выбора папки позволяет упростить данный процесс, если экспорт выполняется часто и нужно обеспечить доступ к определенной папке, чтобы периодически осуществлять программный экспорт нескольких файлов.

Рисунок 1. Приложение Xbox Music использует возможности «Фонотеки»

Рисунок 2. Приложение использует средство выбора файлов для их открытия и сохранения

В Windows 8.1 приложения, использующие библиотеки, могут расширить охват содержимого с помощью управляющих API StorageLibrary. Эти промежуточные API имеют ту же функциональность, что и диалоговое окно свойств библиотеки в Проводнике, где пользователи могут добавлять и удалять папки из библиотек.

Рисунок 3. Диалоговое окно свойств библиотеки

С помощью этих новых функций пользователи могут указывать папки с фотографиями и музыкой непосредственно из приложений, работающих с мультимедийными коллекциями. Хороший пример использования API показан в приложении «Музыка».

Рисунок 4. Приложение «Музыка» — это точка входа в музыкальную коллекцию пользователя для добавления композиций и альбомов

Рисунок 5. После нажатия кнопки Add (Добавить) открывается средство библиотеки для выбора расположения

Это очень просто сделать с помощью API RequestAddFolderAsync. Данный API вызывает средство выбора расположения, чтобы пользователь выбрал папку, которую необходимо добавить в библиотеку. Средство выбора расположения отображает все ошибки напрямую, так что приложению нужно лишь обработать отмену действия. Все это легко сделать несколькими строками кода на JavaScript:

// Отображение окна выбора расположения, чтобы пользователь выбрал папку,
// которую нужно добавить в музыкальную библиотеку 
function addFolder() { 
  Windows.Storage.StorageLibrary.getLibraryAsync(Windows.Storage.KnownLibraryId.music).then(function (library) { 
    return library.requestAddFolderAsync(); 
  }).done(function (folderAdded) { 
    if (folderAdded !== null) { 
      // Папка была добавлена в библиотеку музыки
    } else { 
      // Операция была отменена
    } 
  }); 
}

Это можно также сделать и в C#:

private async void AddFolder() 
{ 
  StorageLibrary picturesLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Music); 
  StorageFolder folderAdded = await musicLibrary.RequestAddFolderAsync(); 
  if (folderAdded != null) 
  { 
    // Папка была добавлена в библиотеку музыки
  } 
  else 
  { 
    // Операция была отменена
  } 
}

Приложение может использовать файлы, не взаимодействуя с файлами и папками пользователя , например, если оно само создает файлы для отслеживания созданного пользователем содержимого. Это могут быть стандартные изображения, аудиофайлы или специальные форматы, как в решениях для управления проектами (например, с использованием XML). Для подобного содержимого, существующего только в контексте данного приложения и бесполезного в других программах, следует использовать папки ApplicationData.

В качестве примера приведем приложение «Звукозапись». Аудиозаписи пользователя целесообразно использовать в контексте приложения, в котором они созданы, поэтому файлы хранятся в папке с данными приложения. Это лучше, чем сохранять файлы в папке «Музыка», поскольку их начинают использовать приложения типа Xbox Music, а так быть не должно. Однако пользователь иногда хочет поделиться этими файлами с коллегами или отредактировать их в приложении для обработки аудио. В таких случаях используются механизмы экспорта, описанные выше.

Рисунок 6. Все аудиофайлы приложения «Звукозапись» хранятся в папке ApplicationData

Доступ к файлам и SkyDrive

Важным нововведением Windows 8.1 является глубокая интеграция SkyDrive с базовыми функциями ОС. Пользователи могут хранить файлы в SkyDrive так же, как и в любой системной папке, и управлять ими независимо от наличия сетевого подключения. Система сама синхронизирует файлы в фоновом режиме, процесс является полностью прозрачным для пользователя.

К SkyDrive привязаны все функции системы:

  • Приложение SkyDrive позволяет просматривать и управлять файлами как в режиме онлайн, так и офлайн.
  • SkyDrive представляет собой область для выбора по умолчанию, благодаря чему пользователи могут импортировать файлы из SkyDrive в приложения и экспортировать их обратно в облако.
  • В Проводнике можно взаимодействовать со SkyDrive так же, как с обычной папкой.

  • Рисунок 7. Управление файлами в приложении SkyDrive

Рисунок 8. SkyDrive в средстве выбора файлов

Рисунок 9. SkyDrive в Проводнике

Будучи разработчиком, воспринимайте файлы SkyDrive как «просто файлы» — они используют тот же интерфейс StorageFile, что и другие файлы в системе, и управление ими осуществляется аналогичным образом. Но если копнуть глубже, между файлами на локальном ПК и в хранилище SkyDrive есть некоторые различия. В Windows 8.1 мы ввели концепциюсмарт-файлов. Смарт-файл — это файл, который отображается в системе как обычный, но при этом хранит метаданные и эскизы, а не содержимое. Содержимое файла расположено на SkyDrive и доступно для загрузки по запросу. Смарт-файлы позволяют уменьшить объем пространства, необходимого для отображения файлов SkyDrive на устройстве.

Вы с этим знакомы, если пользовались контрактом выбора файлов  в Windows 8. В этом контракте пользователи могут открывать средство выбора файлов из Приложения А и выбирать файл из Приложения Б. Приложение А получает StorageFile, но он может быть на самом деле потоковым файлом, содержимое которого загружается на лету из хранилища. Смарт-файлы в SkyDrive работают схожим образом: их содержимое прозрачно загружается при открытии файла. Это значит, что вам не нужно прописывать разные пути для локальных файлов и файлов SkyDrive. Однако некоторые файловые операции (редактирование метаданных или копирование) будут выполняться дольше, поскольку на загрузку файла уходит больше времени, чем на его открытие с диска.

Совет. Приложения для настольных ПК могут обращаться к смарт-файлам, если используют соответствующий API. Смарт-файлы имеют атрибут LocallyIncomplete, обозначающий недоступность их содержимого в локальной среде. Приложения для настольных ПК могут определять этот атрибут и использовать интерфейс IStream для выполнения операций с такими файлами. Поскольку многие приложения Win32 используют другие API, не поддерживающие концепцию смарт-файлов, и при работе с ними могут возникать ошибки, система помечает их атрибутом system hidden (системный скрытый), чтобы предотвратить случайный доступ к ним.

Как и насколько часто использовать SkyDrive, пользователь решает сам: в Windows 8.1 доступ к файлам SkyDrive осуществляется через посредников, так что все всегда остается под контролем пользователя. Именно поэтому чисто программного способа получения доступа к папке пользователя в SkyDrive не существует. Как правило, приложение будет сталкиваться с файлами SkyDrive, если пользователь обратится к этим файлам в средстве выбора, чтобы открыть их в приложении, или если добавит папку SkyDrive в библиотеку, к которой приложение имеет доступ.

Удобство работы в онлайн- и офлайн-режиме

Мы узнали, что в большинстве случаев файл из SkyDrive ведет себя так же, как и локальный файл. Из этого правила есть несколько исключений, возникающих при отсутствии интернет-соединения. В частности, если пользователь работает в автономном режиме, смарт-файлы невозможно загружать по запросу и их функциональность будет ограничена. Приложение, поддерживающее SkyDrive, может распознавать подобные случаи и информировать пользователя об этом, сохраняя функциональность, возможную при работе в автономном режиме.

Рисунок 10. Приложение SkyDrive различает автономные элементы, но позволяет пользователю перемещать или удалять их

WinRT API предоставляет средства для максимально удобной работы независимо от доступности сети. Золотое правило при работе с SkyDrive: программный код нужно писать только один раз, и он действует для всех типов файлов — как локальных, так и файлов в SkyDrive. Типичным примером служит доступность файлов: все файлы в системе имеют свойство IsAvailable, позволяющее выяснить, есть ли доступ к содержимому файла в момент его выбора.

Таблица 1. Значения свойства IsAvailable в различных сценариях

Рисунок 11. API учитывает, что используется тарифицируемое соединение

Ниже перечислены операции, которые выполняются для всех файлов, независимо от доступности сети. В этом случае не нужно проверять свойство IsAvailable перед их выполнением:

Выполнение других операций с недоступным файлом приведет к появлению ошибки. Перед тем как выполнять любую из этих операций в приложении, проверьте значение свойства IsAvailable и отключите команду для любого недоступного файла. Пользователям будет удобнее, если приложение будет упреждать появление возможных проблем, заранее информируя об этом состоянии и предотвращая любое действие, которое может вызвать ошибку. Операции, требующие доступности файла:

  • Открытие содержимого файла.
  • Редактирование файла.
  • Копирование файла или перемещение его за пределы SkyDrive.

Свойство IsAvailable проверяет, существует ли локальный кеш файла, подсоединен ли пользователь к сети и является ли соединение тарифицируемым, так что вы можете ни о чем не беспокоиться и сосредоточиться на своем коде. Это удобно, но вовсе не значит, что больше не нужно обрабатывать ошибки! Свойства IsAvailable оптимизировано для быстрого получения файлов, а не для поддержки их актуального состояния. Это означает, что отсутствует 100%-я гарантия доступности файла, к которому вы пытаетесь получить доступ (например, если пользователь перейдет в автономный режим вскоре после того, как вы проверите свойство). Для подобных случаев в WinRT API есть ошибка ERROR_NO_NETWORK, поэтому приложение поймет, что операция с файлом не удалась по причине отсутствия подключения к сети.

Свойство IsAvailable позволяет визуально дифференцировать элементы, недоступные в интерфейсе. Для выделения элементов используйте наложение (например, 40%-й фильтр прозрачности, как показано на снимке экрана из приложения SkyDrive выше) или текст (например, строку «Недоступен» рядом с названием файла). Кроме того, на панели приложения или в контекстном меню следует отключить любые действия, которые невозможно выполнить с файлом в автономном режиме.

При запросе файла любые изменения свойства IsAvailable инициируют событие contentschanged, что позволяет обновить интерфейс и отобразить актуальное состояние каждого элемента. В JavaScript это можно сделать так:

picker.pickSingleFolderAsync().done(function (folder) {
  if (folder) {
    // Опрос папки
    var query = folder.createFileQuery();
    // Обязательно обновите представление при срабатывании события contentschanged 
    var listener = function () {
      displayItems(query);
    };
    query.addEventListener("contentschanged", listener);
    // Обязательно отключите обработчик событий, когда пользователь больше не просматривает эту страницу
    WinJS.Navigation.addEventListener("navigated", function () {
      query.removeEventListener("contentschanged", listener);
    });
    // Отображение исходное представление
    displayItems(query);
  }
});

function displayItems(query) {
  query.getFilesAsync().done(function (files) {
    // Отображение списка файлов с указанием их доступности 
    files.forEach(function (file) {
      // Создание записи в списке для элемента 
      var list = document.getElementById("itemsList");
      var listItemElement = document.createElement("li");
      listItemElement.textContent = file.name + " (";

      // Отображение доступности элемента (элементы SkyDrive доступны, если 
      // находятся в режиме онлайн или помечены как «Всегда доступны в автономном режиме»)
      if (!file.isAvailable) {
        listItemElement.textContent += "not ";
      }
      listItemElement.textContent += "available)";

      list.appendChild(listItemElement);
    });
  });
}
И похожим образом в C#:
StorageFile folder = await filePicker.PickSingleFileAsync();
  if (folder != null)
  {
    // Опрос папки
    auto query = folder.CreateFileQuery();

    //Обязательно обновите представление при срабатывании события contentschanged
    var eventHandler = new TypedEventHandler<IStorageQueryResultBase, object>( (IStorageQueryResultBase sender, object args) =>
    {
      Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
      {
        displayItems();
      });
    });
    query.ContentsChanged += eventHandler;

    // Обязательно отключите обработчик событий, когда пользователь не просматривает эту страницу
    thisPage.OnNavigatedFrom += () =>
    {
      query.ContentsChanged -= eventHandler;
    }

    // Отображение исходного представления
    displayItems();
  }

private async void displayItems()
{
  IReadOnlyList<StorageFile> files = await query.GetFilesAsync();
  //Отображение списка файлов с указанием их доступности
  foreach (StorageFile file in files)
  {
    string text = "File name: " + file.Name + " (";

    // Отображение доступности элемента (элементы SkyDrive доступны, если 
    // находятся в режиме онлайн или помечены как «Всегда доступны в автономном режиме»)
    if (!file.IsAvailable)
    {
      text += "not ";
    }

    text += "available)";

    print(text);
  }
}

Совет. Если вы используете ListView с привязкой данных к запросу файла, то применяйте IsAvailable в модуле для рендеринга или в шаблоне, чтобы настроить представление элементов в автономном режиме. Пример можно посмотреть в приложении HiloJS на CodePlex.

Кроме того, можно проделать еще несколько операций, чтобы работать с приложением было удобно, независимо от места расположения обрабатываемых файлов.

  • При открытии файла выведите неопределенный индикатор хода выполнения в пользовательском интерфейсе до завершения операции.
    • Он покажет, что приложение отвечает на запрос, даже если файл загружается по медленному соединению.
  • Разрешите пользователям отменятьоперации с файлами.
    • Отмена пригодиться для операций открытия или копирования файлов в SkyDrive, на случай если соединение окажется медленным или пользователь передумает. Все WinRT async API для доступа к файлам поддерживают отмену действий.

И наконец, хотя выше и говорилось о том, что приложение не должно различать файлы SkyDrive и локальные файлы, возможно, ему нужно выделять файлы из определенного источника. Например, нужно отобразить пользовательские элементы, хранящиеся в облаке, отдельно от локальных (таким образом вы покажете, что они доступны на любых устройствах, в то время как локальные — нет). В этом случае следует воспользоваться идентификатором нового свойства Provider для определения источника файла или папки. В Windows 8.1 данный идентификатор принимает одно из четырех значений:

  • computer (файлы на локальном ПК или подключаемом оборудовании, например USB-носителях);
  • SkyDrive (файлы в SkyDrive);
  • network (файлы в локальной сети);
  • app (для файлов, выбранных через контракт выбора файлов).

Совет. StorageProvider имеет фиксированный идентификатор и локализованное свойство DisplayName. Удостоверьтесь, что используете идентификатор для таких операций, как сравнение (на основе порядковых номеров или нечувствительного к регистру), фильтрация и DisplayName для пользовательского интерфейса. В целом старайтесь избегать жестко прописанных идентификаторов, поскольку в будущем могут появиться новые идентификаторы.

Пример использования данного API в JavaScript:

picker.pickSingleFolderAsync().done(function (folder) {
  if (folder) {
    // Опрос папки 
    var query = folder.createFileQuery();
    query.getFilesAsync().done(function (files) {
      // Отображение списка файлов с поставщиком 
      files.forEach(function (file) {
        // Создание записи в списке для элемента 
        var list = document.getElementById("itemsList");
        var listItemElement = document.createElement("li");
        listItemElement.textContent = file.name;

        // Отображение поставщика элемента 
        listItemElement.textContent += ": On " + file.provider.displayName;          
      });
    });
  }
});
//И в C#:
StorageFolder folder = await picker.PickSingleFolderAsync(); 
if (folder != null) 
{ 
  // Опрос папки 
  auto query = folder.CreateFileQuery(); 
  IReadOnlyList<StorageFile> fileList = await query.GetFilesAsync(); 
 
  // Отображение списка файлов с поставщиком хранилища и доступностью 
  foreach (StorageFile file in fileList) 
  { 
    // Создание записи в списке для элемента 
    string line = file.Name; 
 
    // Отображение поставщика элемента (данный ПК, SkyDrive, сеть или содержимое приложений) 
    line += ": On " + file.Provider.DisplayName; 
 
    OutputPanel.Children.Add(CreateLineItemTextBlock(line)); 
  } 
}

Новый подход к отображению изображений

Отображение изображений из пользовательского содержимого — популярный способ персонализации работы пользователя и создания более привлекательного интерфейса. В большинстве случаев приложения просто получают файл и задают его в качестве источника элемента-изображения на собственном полотне. Однако этот подход не даст эффекта, если нужно просто отобразить изображение и приложение не изменяет сам файл. Для редактирования требуется доступ ко всем данным файла, но для вывода на экран нужна лишь версия изображения меньшего размера.

Многие камеры делают фотографии в гораздо большем разрешении, чем разрешение мониторов для ПК, поэтому приложение может попусту расходовать мегабайты трафика. При работе с файлами по сети ситуация усложняется еще больше, и приложение может вызвать негативные эмоции у пользователя, если соединение тарифицируется. И наконец, применение данного подхода к работе со смарт-файлами усложнит код, если приложение должно определять, можно ли отобразить файл.

Чтобы упростить этот процесс, приложение должно использовать эскиз, а не полноразмерное изображение. Windows хранит большой кеш эскизов различного размера, а также специальные эскизы для смарт-файлов (в том числе среднего размера, которые доступны всегда, и большого размера, загружаемые в виде потока из облака). Это означает, что эскизы — лучший и самый быстрый способ получения доступных представлений изображения, не привязанных к сетевому подключению.

В Windows 8.1 максимальный размер эскиза, поддерживаемый локально и в облаке, увеличен до 1600 пикселей; этого достаточно для полноэкранного просмотра на устройствах различных формфакторов. Кроме того, для возврата уменьшенных изображений любого размера создан новый API, так что можно использовать только его, независимо от требуемого для отображения размера. API использует все имеющиеся эскизы и возвращается к самому изображению в случае, если эскизы не подходят по размеру (или к самому большому из найденных эскизов, если изображение недоступно). Возвращенный объект StorageItemThumbnail позволяет с легкостью определить, использовался ли откат.

Пример использования этого API в JavaScript:

file.getScaledImageAsThumbnailAsync(thumbnailMode, requestedSize, thumbnailOptions).done(function (thumbnail) {
  if (thumbnail) {
    // Возвращено изображение
    if (thumbnail.returnedSmallerCachedSize) {
      // Система не смогла получить изображение нужного
      // размера и возвратила доступный эскиз самого большого размера
    }
    // Вы можете легко вставить его в элемент img
    var image = document.getElementById("myImage");
    var url = URL.createObjectUrl(thumbnail, { oneTimeOnly: true });
    image.src = url;
  }
}, function (error) {
  // При получении эскиза возникла ошибка
});
//Аналогичным образом в C#:
using (StorageItemThumbnail thumbnail = await file.GetScaledImageAsThumbnailAsync(thumbnailMode, size, thumbnailOptions)) 
{ 
  if (thumbnail != null) 
  {
    // Возвращено изображение
    if (thumbnail.ReturnedSmallerCachedSize) {
      // Система не смогла получить изображение нужного
      // размера и возвратила доступный эскиз самого большого размера
    }
    // Вы можете легко вставить его в элемент img
    BitmapImage bitmapImage = new BitmapImage(); 
    bitmapImage.SetSource(thumbnail); 
    image.Source = bitmapImage; 
  } 
}

Благодаря этому API доступ к оригинальному файлу нужен только для редактирования, поэтому приложение будет работать максимально быстро — как в режиме онлайн, так и автономно.

Подведем итоги

В данной статье мы продемонстрировали, как тесно интегрирован SkyDrive с Windows 8.1 и моделью доступа к файлам Windows 8 для приложений Магазина Windows. Несколько новых свойств существенно упрощают взаимодействие приложений со SkyDrive благодаря определению доступности файла и его источника. Использование эскизов в качестве заменителей изображений позволит стабилизировать работу приложения при нестабильном сетевом подключении и быстрее отображать изображения. Большинство приложений, использующих пользовательские файлы, можно обновить с помощью нескольких строк кода, не создавая новые ветки кода для файлов SkyDrive.

Марк Вотье (Marc Wautier), руководитель программ, Windows User Experience