Разработка приложений для Modern UI: Windows 8

Работа с файлами

Показывать лекцию целиком

Цель изучения

Материалы к модулю Вы можете скачать здесь.

В этом модуле вы изучите ограничения доступа в приложениях для Магазина Windows, способы настройки манифеста приложения для разрешения доступа к файлам, и средствам выбора файлов (File Pickers) для доступа к пользовательским файлам. Также вы узнаете о том, как можно использовать класс StorageFolder для сохранения специфических данных приложения.

Начало работы

Вам понадобится решение Starter из подпапки \Module_6_Assets\Starter. Это итоговый проект из предыдущего модуля.

Доступ к файлам в WinRT-приложениях

В отличие от традиционных приложений Windows, WinRT-приложения по умолчанию ограничивают доступ к пользовательским данным и устройствам. Если WinRT-приложение требует разрешения только на чтение одного или нескольких файлов, то, скорее всего, можно использовать средство выбора файлов (File Picker) с настройками безопасности, установленными по умолчанию.

При помощи элемента управления File Picker можно:

  • разрешить пользователю просматривать и выбирать один или несколько файлов с устройства;
  • разрешать пользователю выбирать место сохранения файла.
  • Также можно использовать File Picker расширенно: для доступа приложений к файлам или доступа приложения к другому приложению.

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

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

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

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

    Упражнение: исследование файла манифеста

    Для того, чтобы объявить возможности в приложении, необходимо редактировать манифест приложения, который является XML-файлом. В Visual Studio есть специальный редактор для редактирования этого файла.

  • Откройте решение Starter из \Module_6_Assets\Starter.
  • Найдите файл package.appmanifest в проекте.
  • Дважды кликните файл для открытия редактора.
  • Перейдите на вкладку Возможности (Capabilities Tab). Здесь есть список всех возможностей, которые можно объявить в приложении. Для просмотра описания этих возможностей посетите страницу: http://msdn.microsoft.com/en-us/library/windows/apps/hh464936.aspx
  • Теперь перейдите на закладку Объявления (Declarations). Эта закладка показывает список доступных контрактов. Контракты описывают, каким образом приложение может взаимодействовать с другими приложениями и с платформой Windows для таких функций, как поиск и совместное использование данных. Контракты будут более подробно рассмотрены в 8 модуле.
  • Promise-объект для асинхронных операций

    Доступ к файлам в WinRT осуществляется асинхронно. Это означает, что процесс доступа к файлу начинается отдельным потоком, а когда файловая операция завершена, происходит возвращение к вызывающему потоку. Фактически, эти операции происходят параллельно с другими (ввод данных, анимация и пр.).

    В WinJS есть promise-объект, необходимый для помощи в асинхронных операциях. Promise-объект может быть использован для всех асинхронных операций, включая web-запросы, файловые операции и др., поэтому важно хорошо понимать принцип его действия.

    Базовая структура вызова с использованием Promise-объекта выглядит следующим образом (псевдокод):

    myObject.doSomethingAsync()
        .then(onCompleteFunction1, onErrorFunction1, onProgressUpdate1)
    

    myObject

    определённый объект, например, объект запроса файла или ссылки.

    doSomethingAsync

    использование асинхронного метода, например, запрос на открытие файла или загрузка WEB-страницы

    then

    определяет, что произойдёт, когда асинхронная функция завершится или произойдет ошибка

    onCompleteFunction1

    функция, которая будет выполняться после завершения асинхронной операции

    onErrorFunction1

    (опционально) функция, которая будет выполняться, если во время выполнения асинхронной операции возникнет ошибка или исключение

    onProgressUpdate1

    (опционально) функция, которая будет выполняться, если promise-объект производит обновление элемента управления, позволяющего судить о ходе длительной операции. Обновления прогресса операции не обязательны, но они удобны в продолжительных операциях, где можно показать пользователю визуально, что процесс идёт (как progress bar – строка состояния)

    Можно многократно объединять функции then(). Это бывает полезно, когда необходимо использовать несколько асинхронных операций, к примеру, получение данных от веб-сервиса и их последующее сохранение в файл.

    Объединение нескольких методов then() может выглядеть так (псевдокод):

    myObject.doSomethingAsync()
        .then(onCompleteFunction1, onErrorFunction1, onProgressUpdate1)
        .then(onCompleteFunction2, onErrorFunction2, onProgressUpdate2)
    

    Другая важная функция promise – это метод done(). Метод done() похож на метод then() за исключением того, что он может инициировать ошибки, которые могут произойти. Полезно помещать эту функцию последней в цепочке функций, так как этот метод может перехватывать любые ошибки метода then() и инициировать или обрабатывать их.

    Использование метода done() совместно с методом then() может выглядеть так:

    myObject.doSomethingAsync()
        .then(onCompleteFunction1, onErrorFunction1, onProgressUpdate1)
        .done(onCompleteFunction2, onErrorFunction2, onProgressUpdate2)
    

    Упражнение: использование File Picker (средства выбора файла)

    Теперь вы имеете представление о том, как функционируют возможности (Capabilities) и средства асинхронного исполнения задач (Promises). Сейчас рассмотрим средство выбора файла (File Picker) для выбора изображения из библиотеки пользователя.

  • Нажмите правой кнопкой мыши на подпапку /js/ и выбрать Добавить > Создать элемент (Add > New Item)
  • "Выберите Файл JavaScript (JavaScript File" и введите imageUtils.js.
  • Добавьте код, показанный ниже. Буден создано пространство имён под именем "ImageUtils". В этом пространстве имён появится функция под названием pickAPicture(), которая использует управление средством выбора файла, чтобы разрешить пользователю выбрать файл. Она также представляет переменную под названием picturePicked, так что выбранный файл будет позже доступен в коде.

    Этот код находится в файле \Module_6_Assets\snippets\filePicker.txt

    (function () {
        "use strict";
    
        function pickAPicture() {
            var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
            openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
            openPicker.suggestedStartLocation = 
                 Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
            openPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
    
            openPicker.pickSingleFileAsync().then(function (file) {
                if (file) {
                    ImageUtils.picturePicked = file;
                }
    
  • Откройте файл default.html и добавьте в него следующий скрипт. Он добавит ссылку на файл в новый файл JavaScript, imageUtils.js.

    Этот код находится в файле \Module_6_Assets\snippets\includeImageUtils.txt

    <!-- Add in the code to manage file pick -->
    <script src="/js/imageUtils.js"></script>
  • Откройте файл JavaScript из /controls/addImageFlyout.js.
  • Добавьте следующие строки кода в конец метода инициализации (Initialize), сразу после addEventListener нужно вызвать btnAddItem. Этот код сначала создаёт переменную btnSelectImage, которая ссылается на кнопку выбора изображения во всплывающем меню. Затем создаётся переменная btnAppBarAddImage, которая ссылается на кнопку AppBarCommand на панели приложения. Далее, свойство закрепления (sticky) устанавливается в значение "истина" для всплывающего меню, которое удерживает его открытым, пока пользователь выбирает изображение. И в конце необходимо установить событие для щелчка на кнопке btnSelectImage.

    Этот код находится в файле \Module_6_Assets\snippets\includeImageUtils.txt

    var btnSelectImage = document.getElementById("btnSelectImage");
    var appbar = document.getElementById("appbar");
    var btnAppBarAddImage = document.getElementById("btnAppBarAddImage");
    appbar.winControl._sticky = true;
    btnAppBarAddImage.winControl.flyout._sticky = true;
    
  • Запустите проект. Переведите в рабочее положение панель приложения и выберите Новый элементm (New Ite), затем нажмите на кнопку Выбор изображения (Select Image) . Нужно обратить внимание, что средство выбора файла используется для того, чтобы разрешить выбор файла. Далее вы будете сохранять выбранный файл.
  • Сохранение данных приложения

    Время от времени приложению может быть необходимо сохранить данные или настройки на диске. К примеру, требуется сохранить пользовательские настройки или список файлов. Для этих целей нужно использовать Local Application Data (локальные данные приложения).

    Пользователю также могут понадобиться эти настройки для синхронизации между всеми устройствами. Можно использовать Roaming Application Data (перемещаемые данные приложения) для разрешения синхронизации между пользовательскими устройствами.

    Некоторым приложениям могут потребоваться Temporary Application Data (временные данные) – к примеру, файлы, которые кэшируются в течение одного сеанса работы с приложением, или промежуточные файлы. Нельзя гарантировать, что после завершения работы приложения такие файлы останутся, пользователь может выполнить очистку диска для их удаления.

    Хранение данныхОписание
    Local Application Data (Локальные данные приложения)Расположены на текущем носителе до тех пор, пока приложение или пользователь не удалит их
    Roaming Application Data (Перемещаемые данные приложения)Расположены на всех устройствах, на которые установлено приложение (механизм их работы основан на синхронизации)
    Temporary Application Data (Временные данные приложения)Расположены на текущем носителе, но могут быть удалены системой в любой момент

    Описанные выше и сохранённые в папках на диске файлы хранения данных находятся в папке C:\Users\{Username}\AppData\Local\Packages\{package family ID}\

    … где {Username} – это идентификатор пользователя в Windows, {package family ID} – это идентификатор пакета приложения, который можно найти на вкладке Упаковка (Packaging) в редакторе манифеста приложения.

    Упражнение: сохранение данных приложения

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

  • Откройте файл /js/data.js
  • Добавьте новую группу под названием MyImages (Мои изображения), вставив выделенный код сразу после группы Baseball (Бейсбол).

    Этот код доступен в файле \Module_6_Assets\snippets\addInMyImagesGroup.txt

    var sampleGroups = [
        { key: "Basketball", title: "Basketball", subtitle: "Search results …
        { key: "Soccer", title: "Soccer", subtitle: "Search results for Soccer …
        { key: "Baseball", title: "Baseball", subtitle: "Search results for …
    
        // Add in "My Images"
        {     
            key: "MyImages", title: "My Images",
            subtitle: "My Image Collection",
            backgroundImage: "/images/groupMyImages.png",
            description: "This group contains images added by the user."
        },
    ];
    
  • Затем добавьте в data.js функцию, которая читает файл изображения с диска, создаёт новый элемент данных из группы MyImages и преобразует эту группу в массив для того, чтобы она могла быть сериализована и записана на диск. ( Можно добавить эту функцию сразу после функции getItemsFromGroup).

    Этот код находится в файле \Module_6_Assets\snippets\addInMyImagesGroup.txt

    function addToMyImages(title, description, imageFile) {
    
        var imageFileBase64;
    
        imageFile.openAsync(Windows.Storage.FileAccessMode.read).then(function (_stream) {
            var inputStream = _stream;
            var reader = new Windows.Storage.Streams.DataReader(inputStream);
            var size = inputStream.size;
            if (size > 0) {
                reader.loadAsync(size).then(function () {
                    var b = reader.readBuffer(size);
                    imageFileBase64 = 
                       Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(b);
    
                    imageFileBase64 = "data:image/png;base64," + imageFileBase64;
    
                    // Add a new item to "My Images"
                    var item = { group: sampleGroups[3], title: title, subtitle: imageFile.name, 
    description: description, content: itemContent, backgroundImage: imageFileBase64 };
                    list.push(item);
    
                    // filter out just the "My Images" group items, and convert to an ARRAY 
    // with slice(0)... this is so it can be serialized properly!
                    var myImagesGroup = getItemsFromGroup(sampleGroups[3]).slice(0);
    
                    // serialize to JSON for storage as string
                    var myImagesSerialized = JSON.stringify(myImagesGroup);
    
                    // SAVE to local storage 
                    var applicationData = Windows.Storage.ApplicationData.current;
                    var localFolder = applicationData.localFolder;
    
                    localFolder.createFileAsync("myimages.txt",
        Windows.Storage.CreationCollisionOption.replaceExisting)
                        .then(function (sampleFile) {
                            return Windows.Storage.FileIO.writeTextAsync(sampleFile, 
    myImagesSerialized);
                        });
    
                });
            }
    
        });
    }
    

    О сериализации

    В коде, приведённом выше, вы сохранили массив объектов на диск с применением сериализации. Сериализация – это процесс преобразования объектов в формат хранения, подходящей для записи на диск или сетевой передачи. Здесь используется специальный объект JSON, который позволяет выполнять сериализацию в формате JavaScript Object Notation (объектная нотация JavaScript).

    Вы узнаете о JSON в 7 модуле.

  • Вам необходимо, чтобы функция addToMyImages была доступна для вызова за пределами data.js, поэтому необходимо сделать её внешней (public), соответствующим образом описав ее при объявлении пространства имен. Добавьте выделенный код внизу data.js, в разделе объявления пространства имен.
    WinJS.Namespace.define("Data", {
        items: groupedItems,
        groups: groupedItems.groups,
        getItemsFromGroup: getItemsFromGroup,
        getItemReference: getItemReference,
        resolveGroupReference: resolveGroupReference,
        resolveItemReference: resolveItemReference,
        addToMyImages: addToMyImages
    });
    
  • Откройте /js/addImageFlyout.js
  • Найдите вызов btnAddItem.addEventListener в функции инициализации. Удалите код диалогового сообщения (Message Dialog) и замените его выделенными строками ниже. Этот код выполняется, когда нажимается кнопка Добавить элемент, и он скрывает всплывающее меню и панель приложения и сохраняет данные изображения.
    btnAddItem.addEventListener("click", function (e) {
        // the following code will be executed when btnAddItem is clicked:
        var title = document.getElementById("title");
        var description = document.getElementById("description");
                
        btnAppBarAddImage.winControl.flyout.hide();
        appbar.winControl._sticky = false;
        // save the new item to the data
        Data.addToMyImages(title.value, description.value, 
    ImageUtils.picturePicked);
    });
    
  • Итоги

    В некоторых случаях для доступа к пользовательским файлам в приложениях необходимо объявлять возможности. Иногда File Picker (средство выбора файла) пригодно для того, чтобы разрешить пользователю задействовать файлы в приложении. Но независимо от того, какой доступ необходим в данный момент, важно понимать асинхронную модель. Необходимо знать, когда должны использоваться методы 'then' и 'done'.

    Самостоятельная работа

    В упражнениях этого модуля вы сохраняли пользовательские изображения на локальный носитель. Каким образом можно сделать эти данные перемещаемыми (roaming), чтобы этим можно было пользоваться на всех пользовательских устройствах? Выясните это по нижеприведенной ссылке, после чего добавьте возможности по работе с перемещаемыми данными (roaming data) в ваше приложение.

    http://msdn.microsoft.com/en-us/library/windows/apps/hh465123.aspx

    Вопросы

  • Имеют ли WinRT-приложения полный доступ к пользовательским файлам по умолчанию?
  • Когда необходимо объявлять возможности в манифесте приложения?
  • Как можно сохранить пользовательские настройки в приложении?
  • Дополнительные ресурсы

    Вы можете узнать больше о работе с файлами и о разрешениях, зайдя по следующим ссылкам.

  • Доступ к файлам и разрешения

    http://msdn.microsoft.com/en-us/library/windows/apps/hh967755.aspx

    Подробности о доступе к файлам и разрешений, необходимым для операций с ними.

  • Объявление характеристик в приложении

    http://msdn.microsoft.com/en-us/library/windows/apps/hh464936.aspx

    Описание полного списка возможностей, которые можно объявлять в приложении.

  • Асинхронное программирование на JavaScript

    http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

    Описание разработки ПО с использованием асинхронного режима.

  • Ответы

  • Нет. Для программируемого доступа к целой библиотеке, например, библиотеке музыки, необходимо объявлять возможности в манифесте приложения.
  • Если необходим программируемый доступ к целой библиотеке.
  • Пространство имён Windows.Storage.ApplicationData разрешает сохранение специальных пользовательских данных, используя локальное хранилище данных.
  • Вернуться к учебному плану