Опубликован: 10.04.2013 | Доступ: свободный | Студентов: 440 / 9 | Длительность: 16:52:00
Специальности: Программист
Лекция 2:

Состояния, параметры, файлы и документы

Управление папками и файлами

Так как часто очень удобно сохранять данные приложения напрямую в файл, настало время для того, чтобы внимательно рассмотреть файловые API приложений для Магазина Windows.

Для начала, однако, взглянем на другие API, такие, как URL.createObjectURL – работающее с тем, что мы называем большим двоичным объектом (blob), делая возможным выполнять множество действий в приложения без необходимости спускаться на уровень файлового ввода/вывода. Мы уже видели, как этим пользоваться, устанавливая свойство src для элемента img, тот же подход работает и для других элементов, таких, как audio, video и iframe. Файловые операции ввода-вывода, связанные с подобными действиями, инкапсулируются в createObjectURL. Есть и другие способы использовать большие двоичные объекты, такой, как конверсия элемента canvas с помощью команды canvas.msToBlob в то, что можно присвоить элементу img, и получение двоичных данных большого размера от WinJS.xhr, сохраняя их в файл и затем задавая этот файл в качестве источника для img. Больше об этом вы можете узнать в лекции 5, так же вы можете рассмотреть пример "Использование больших двоичных объектов для сохранения и загрузки содержимого" (http://code.msdn.microsoft.com/windowsapps/Blob-Sample-0e35889e).

Для того чтобы работать напрямую с файлами, рассмотрим то, что имеется в нашем распоряжении, использовав конкретные образцы из примера "Доступ к файлам" (http://code.msdn.microsoft.com/windowsapps/File-access-sample-d723e597).

Основные API WinRT для работы с файлами расположены в пространстве имен Windows.Storage. Ключевую роль играют классы StorageFolder (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefolder.aspx) и StorageFile (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefile.aspx). И на то и на другое иногда ссылаются как на "элементы хранения", так как и тот и другой класс унаследованы от IStorageItem (http://msdn.microsoft.com/library/windows/apps/windows.storage.istorageitem.aspx) и имеют одинаковые свойства, такие, как name, path, dateCreated и attributes, а так же методы deleteAsync и renameAsync.

Файловые операции ввода-вывода в WinRT практически всегда начинаются с получения объекта StorageFolder посредством одного из нижеперечисленных способов. В некоторых случаях вы так же можете получить объект StorageFile напрямую:

  • Свойство Windows.ApplicationModel.Package.current.installedLocation (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.package.installedlocation.aspx) получает StorageFolder, посредством которого вы можете загружать данные из файлов, которые находятся в пакете приложения (все файлы – только для чтения).
  • Свойства Windows.Storage.ApplicationData.current.localFolder, roamingFolder, или temporaryFolder обеспечивают объекты StorageFolder для различных расположений данных приложения (для чтения и записи).
  • Приложение может позволить пользователю выбирать папку или файл напрямую, используя средство выбора файлов, запущенное посредством Windows.Storage.Pickers.FolderPicker (http://msdn.microsoft.com/library/windows/apps/windows.storage.pickers.folderpicker.aspx) а так же FileOpenPicker (http://msdn.microsoft.com/library/windows/apps/windows.storage.pickers.fileopenpicker.aspx) и FileSavePicker (http://msdn.microsoft.com/library/windows/apps/windows.storage.pickers.filesavepicker.aspx). Для приложений это – предпочтительный способ, используя который нет необходимости в просмотре содержимого библиотеки (смотрите следующий пункт списка). Это так же единственное средство, с помощью которого приложение может получить доступ к безопасным (несистемным) областям файловой системы без необходимости объявления дополнительных возможностей в манифесте.
  • Объект Windows.Storage.KnownFolders (http://msdn.microsoft.com/library/windows/apps/windows.storage.knownfolders.aspx) предоставляет объекты StorageFolder для библиотек "Документы", "Музыка", "Видео", так же, как и для доступа к съемным устройствам. Задавая соответствующие возможности в манифесте, вы можете работать с содержимых этих папок. (Попытка получить доступ к папке без объявления соответствующих возможностей приведет к выдаче исключения "Доступ запрещен").
  • Объект Windows.Storage.DownloadsFolder (http://msdn.microsoft.com/library/windows/apps/windows.storage.downloadsfolder.aspx) предоставляет метод createFolderAsync, посредством которого вы можете получить StorageFolder для папки "Загрузки". Он так же предоставляет метод createFileAsync для непосредственного создания объектов StorageFile. Данное API следует использовать, если ваше приложение управляет загруженными файлами напрямую. Обратите внимание на то, что DownloadFolder предоставляет лишь эти два метода, она не является StorageFolder.
  • Статический метод Windows.Storage.StorageFolder.getFolderFromPathAsync (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefolder.getfolderfrompathasync.aspx) возвращает StorageFolder для заданного пути, если и только если у вашего приложения уже есть разрешение на доступ к данному расположению. В противном случае вы получите исключение "Доступ запрещен". Похожий статический метод существует и для работы с файлами, он называется Windows.Storage.StorageFile.getFileFromPathAsync (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefile.getfilefrompathasync.aspx), к нему применимы те же ограничения. Windows.Storage.StorageFile.getFileFromApplicationUriAsync (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefile.getfilefromapplicationuriasync.aspx) открывает файлы с помощью URI ms-appx:// (package) и ms-appdata:/// . Другие схемы не поддерживаются.
  • Как только объект, соответствующий папке или файлу, получен, его можно сохранить в кэше Windows.Storage.AccessCache (http://msdn.microsoft.com/library/windows/apps/windows.storage.accesscache.aspx), который позволяет приложению получать эти объекты позже, с теми же программными разрешениями. Это нужно, преимущественно, для файлов или папок, выбранным с помощью средств выбора, так как разрешения на доступ к элементу хранения выдаются лишь на период существования данного объекта в памяти. Всегда следует использовать данное API, как показано в Сценарии 6 примера о доступе к файлам, где вы принимаете решение о сохранении пути к файлу. Опять же, StorageFolder.getFolderFromPathAsync и StorageFile.getFileFromPathAsync выдадут исключение "Доступ запрещен", если они указывают на любое расположение, для доступа к которому у вас уже нет разрешения. Пути к файлам, так же, не будут работать для файлов, предоставленных другими приложениями через средство выбора файлов, так как объект StorageFile, на самом деле, может не указывать на файл, который реально существует в файловой системе.

Как только у вас есть StorageFolder, вы можете выполнять с ним вполне ожидаемые действия: получать свойства папки (в том числе – миниатюру), создавать и/или открывать файлы и папки, просматривать содержимое папки. В случае с последним действием, существует, конечно, API для получения списка содержимого папки (смотрите метод getItemsAsync), но чаще нужно получить ограниченный список содержимого, отобранного на основе определенных критериев, вместе с миниатюрами и другими индексированными метаданными файлов (музыкальные альбомы и сведения о записях, подписи к изображениям, теги и так далее), которые вы можете использовать для группировки и организации этих файлов. Это – цель запросов (queries), направленных на файлы, папки или элементы хранения (файлы + папки), которыми можно управлять посредством методов createFileQuery[WithOptions] , createFolderQuery[WithOptions] , и createItemQuery[WithOptions] . Мы уже видели некоторые примеры этих механизмов в приложении FlipView, которые мы построили с использованием Библиотеки "Изображения" в лекции 5 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", и мы вернемся к этой теме в контексте пользовательских данных, основного сценария для использования запросов, в конце этой лекции1Получение свойств папки происходит посредством метода элемента хранения getBasicPropertiesAsync (http://msdn.microsoft.com/library/windows/apps/windows.storage.storagefolder.getbasicpropertiesasync.aspx) (подключенного для StorageFolder , но доступного и StorageFile .) Этот вызов предоставляет объект Windows.Storage.FileProperties.BasicPropertiesClass (http://msdn.microsoft.com/library/windows/apps/windows.storage.fileproperties.basicproperties.aspx), который, в свою очередь, имеет метод retrievePropertiesAsync (http://msdn.microsoft.com/ru-ru/library/windows/apps/br212124.aspx). С помощью этого метода вы можете получить любое количество свойств Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977.aspx). Свойство наподобие System.FreeSpace даст вам реальный размер свободного пространства на диске, где расположена папка, которой соответствует StorageFolder . .

Совет. Существуют некоторые расширения имен файлов, таких, как .lnk, .url и другие, которые зарезервированы системой и не могут быть перечислены. Их полный список можно найти в материале "Обработка активации файлов" (http://msdn.microsoft.com/library/windows/apps/hh452684.aspx). Кроме того обратите внимание на то, что доступ к UNC-путям требует объявления возможностей Частные сети (клиент и сервер) (Private Networks (ClientServer)) и Корпоративная аутентификация (Enterprise Authentication) в манифесте вместе с объявлением типов файлов, к которым вы хотите получить доступ.

С помощью любого StorageFolder, в особенности для расположений, соответствующих данным приложения, вы можете создать любую необходимую структуру папок с помощью методов createFolderAsync/getFolderAsync, которые дают вам больше объектов StorageFolder. В любой из этих папок вы затем используете методы createFileAsync/getFileAsync для доступа к отдельным файлам, каждый из которых будет представлен в виде объекта StorageFile.

Каждый StorageFile (http://msdn.microsoft.com/library/windows/apps/br227171.aspx) обеспечивает соответствующие свойства наподобие name, path, dateCreated, fileType, contentType, и attributes, конечно, вместе с методами наподобие getThumbnailAsync, copyAsync, deleteAsync, moveAsync, moveAndReplaceAsync, и renameAsync для управления файлами. Файл можно открыть разными способами, зависящими от того, доступ какого рода вам нужен:

  • Методы openAsync и openReadAsync предоставляют потоки для произвольного побайтового доступа для чтения и записи. Потоки – это объекты IRandomAccessStream (http://msdn.microsoft.com/library/windows/apps/hh438400.aspx) и IRandomAccessStreamWithContentType (http://msdn.microsoft.com/library/windows/apps/windows.storage.streams.irandomaccessstreamwithcontenttype.aspx), соответственно, оба находящиеся в пространстве имен Windows.Storage.Streams. Первый из них работает с исходным двоичным потоком. Второй работает с данными, используя их тип, что может быть нужно для работы с HTTP-ответом, который добавляет в поток данных сведения о типе содержимого.
  • Метод openSequentialReadAsync предоставляет объект только для чтения Windows.Storage.Streams.IInputStream (http://msdn.microsoft.com/library/windows/apps/br241718.aspx), посредством которого можно читать содержимое файла блоками байтов, но нельзя возвращаться к ранее прочитанной области. Данный метод следует использовать во всех случаях, когда вам просто нужно прочесть данные из потока, так как он обладает лучшей производительностью, чем поток для произвольного доступа (источник можно оптимизировать для последовательного чтения данных).
  • Метод openTransactedWriteAsync предоставляет объект Windows.Storage.StorageStreamTransaction (http://msdn.microsoft.com/library/windows/apps/hh996767.aspx), который является вспомогательным объектом, построенным на основе IRandomAccessStream , который имеет методы commitAsync и close для обработки транзакций. Это необходимо при сохранении данных со сложной структурой, для того, чтобы быть уверенным в том, что вся операция записи произошла атомарным образом, и файлы не были повреждены при ее прерывании. Сценарий 4 в примере о доступе к файлам показывает этот подход.

Класс StorageFile так же предоставляет два статических метода: createStreamedFileAsync и createStreamedFileFromUriAsync. Результатом их работы является объект StorageFile, который обычно передают другим приложениям посредством контракта, как мы увидим в лекции 1 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой". Полезность этих методов заключается в том, что доступ к файлу, лежащему в их основе, не осуществляется до тех пор, пока его данные не будут запрошены в первый раз, если такой запрос вообще произойдет.

Соберем теперь все это воедино. Вот небольшой фрагмент кода, использующий исходные API, которые мы рассматривали, для создания и открытия файла "data.tmp" в папке временных данных приложения, расположенной в папке AppData, и для записи в него заданной строки. Это – код из упражнения RawWriteFile к этой лекции. Позвольте мне пояснить, что то, что здесь показано, использует низший уровень API в WinRT для этих целей, и это не то, чем обычно пользуются, как мы увидим в следующем разделе. Тем не менее, это полезно знать, так как бывают случаи, когда нужно использовать нечто подобное:

var fileContents = "Congratulations, you're written data to a temp file!";
writeTempFileRaw("data.tmp", fileContents);


function writeTempFileRaw(filename, contents) {
var tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder;
var outputStream;

//Ей-богу!
tempFolder.createFileAsync(filename, Windows.Storage.CreationCollisionOption.replaceExisting)
.then(function (file) {
return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
}).then(function (stream) {
outputStream = stream.getOutputStreamAt(0);
var writer = new Windows.Storage.Streams.DataWriter(outputStream);
writer.writeString(contents);
return writer.storeAsync();
}).done();
}
 

Хорошо, что недавно мы узнали об асинхронных операциях, объединенных в цепочку! Для начала мы создаем или открываем заданный файл в папке temporaryFolder среди данных приложения (createFileAsync), затем получаем выходной поток для файла (openAsync и getOutputStream). Затем мы создаем на основе потока объект DataWriter, записываем в файл данные (writeString) и убеждаемся, что информация сохранена в файле (storeAsync).

Но вы скажете: "Вы, должно быть, шутите! Четыре объединенных в цепочку асинхронных операции только для того, чтобы записать обычную строку в файл! Кто разрабатывал это API?" Действительно, когда мы начинали создавать самое первое приложение для Магазина Windows в Microsoft, у нас было лишь это, и мы задавали себе те же вопросы. В конце концов, выполнение некоторых ожидаемо простых файловых операций ввода-вывода – это обычно первое, что добавляют в приложение "Hello World", и это в данном случае совсем не просто. Что еще хуже, тогда у нас не было promise-объектов для асинхронных операций в JavaScript, поэтому мы должны были написать это на основе исходных вложенных операций. Такие были времена.

К счастью, более простые API уже были доступны, и многое было сделано уже после этого. Это те API, которые вы обычно будете использовать, при работе с файлами, и это мы рассмотрим в следующем разделе. Тем не менее, важно понимать структуру вышеприведенного низкоуровневого кода, так как класс Window.Storage.Streams.DataWriter (http://msdn.microsoft.com/library/windows/apps/br208154.aspx) и родственный ему DataReader (http://msdn.microsoft.com/library/windows/apps/windows.storage.streams.datareader.aspx) – это очень важные механизмы для работы с множеством различных потоков ввода-вывода, и они необходимы для процессов кодирования данных. Контроль над мелкими деталями, кроме того, позволяет реализовывать сценарии, когда различные компоненты вашего приложения вносят собственный вклад в структуру файла. Поэтому хорошо будет взглянуть на документацию по ним, и на пример "Чтение и запись данных" (http://code.msdn.microsoft.com/windowsapps/Reading-and-writing-data-75ea10a3) лишь для того, чтобы вы были знакомы с их возможностями.

Врезка: Закрытие потоков против закрытия файлов

Разработчики, которые работали ранее с API ввода-вывода иногда спрашивают, почему у объекта StorageFile нет чего-то вроде метода close. Причина в том, что StorageFile представляет собой объект, соответствующий файлу, но не поток данных, посредством которого можно получать доступ к содержимому файла. Таким образом, когда вы вызываете методы вроде StorageFile.openAsync для получения потока, при этом файл открывается, и файл закрывается только при закрытии потока посредством его метода close.

В вышеприведенном коде вы не увидите подобного вызова, так как и DataReader, и DataWriter заботятся о подобных деталях для вас, когда такие вызовы опущены. Однако если вы отделите поток от этих объектов посредством их методов detachStream, вы ответственны за вызов метода потока close .

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

FileIO, PathIO и вспомогательные классы WinJS (а так же FileReader)

Простота – это очень хорошо, если речь идет о файловых операциях ввода-вывода, и дизайнеры WinRT убедились в том, что наиболее распространенные сценарии не нуждаются в длинных цепочках асинхронных операций, которые мы видели в предыдущем разделе. Классы Windows.Storage.FileIO (http://msdn.microsoft.com/library/windows/apps/windows.storage.fileio.aspx) и PathIO (http://msdn.microsoft.com/library/windows/apps/windows.storage.pathio.aspx) предоставляют усовершенствованный интерфейс, а разница между ними заключается в том, что методы FileIO принимают параметр типа StorageFile, а методы PathIO принимают строковое имя файла. В остальном они имеют одинаковые методы, в частности, [read | write]BufferAsyn c (работают с байтовыми массивами), [append | read | write]LinesAsync (работают со строковыми массивами),и [append | read | write]TextAsync (работают с отдельными строками). В последнем случае класс WinJS.IOHelper предоставляет даже более простой интерфейс посредством своих методов readText и writeText.

Посмотрим, как все это работает, начнем с нескольких образцов кода из примера "Доступ к файлу" (http://code.msdn.microsoft.com/windowsapps/File-access-sample-d723e597). Сценарий 2 демонстрирует запись текстовой строки из элемента управления в файл (этот код упрощен, для большей понятности):

 var userContent = textArea.innerText;

//sampleFile создается при старте с помощью Windows.Storage.KnownFolders.documentsLibrary.getFileAsync
Windows.Storage.FileIO.writeTextAsync(sampleFile, userContent).done(function () {	
outputDiv.innerHTML = "The following text was written to '" + sampleFile.name	
+ "':<br /><br />" + userContent;	
});

Для сравнения с кодом из предыдущего раздела, мы можем заменить все, что касается потоков и объекта DataWriter, одной строкой кода:

tempFolder.createFileAsync(filename, Windows.Storage.CreationCollisionOption.replaceExisting)
.then(function (file) {	
Windows.Storage.FileIO.writeTextAsync(file, contents).done();	
})
 

Чтобы сделать это еще проще, объект WinJS.Application.temp (WinJS.Application.IOHelper ( http://msdn.microsoft.com/library/windows/apps/br211764.aspx )) сокращает все до одной строки (которая является асинхронной операцией и возвращает promise-объект:

WinJS.Application.temp.writeText(file, contents); 

Чтение текста посредством асинхронного метода readText так же просто, и WinJS предоставляет тот же интерфейс для папок local и roaming, вместе с двумя другими методами – exists и remove2Если вам интересно, почему асинхронные методы, такие, как readText и writeText не имеют слова Async в своих названиях, это потому, что дизайнеры WinJS сознательно решили следовать существующим соглашениям об именам, принятых в JavaScript, где такой суффикс обычно не используется. API WinRT, с другой стороны, с другой стороны, не зависит от языков программирования, и имеет собственное соглашение об именовании, допускающее использование суффикса Async. . Тем не менее, эти вспомогательные функции WinJS доступны лишь для папок в AppData, но не для других областей файловой системы. Для работы с другими областями следует использовать классы FileIO и PathIO.

Кроме того, вы можете пользоваться классом HTML5 FileReader, который доступен приложениям для Магазина Windows, и который является частью спецификации "W3C File API" (http://www.w3.org/TR/FileAPI/#dfn-filereader). Как следует из его названия (reader), он подходит только для чтения файлов и не обладает возможностью записи в них, но одно из его преимущества заключается в том, что он может работать не только с файлами, но и с большими двоичными объектами. Некоторые образцы подобных действий можно найти в примере "Использование больших двоичных объектов для сохранения и загрузки содержимого" (http://code.msdn.microsoft.com/windowsapps/Blob-Sample-0e35889e).

Шифрование и сжатие

WinRT предоставляет две возможности, которые может быть весьма полезны для управления состоянием приложения: шифрование и сжатие данных.

Возможности шифрования предоставляют API Windows.Security.Cryptography (http://msdn.microsoft.com/library/windows/apps/br241404.aspx) и Windows.Security.Cryptography.Core (http://msdn.microsoft.com/library/windows/apps/windows.security.cryptography.core.aspx). Первое содержит методы для базовых операций кодирования и декодирования (для форматов base64, hex, text). Второе занимается шифрованием в соответствии с различными алгоритмами. Как показано в примере "Сохранение секретных данных с помощью шифрования" (http://code.msdn.microsoft.com/windowsapps/Secret-Saver-f8a69623), данные обычно кодируют каким-либо образом с использованием метода Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary, а затем создают или получают алгоритм и передают его вместе с буфером данных методу Windows.Security.Cryptography.Core.CryptographicEngine.encrypt. Другие методы, такие как decrypt и convertBinaryToString, выполняют обратные операции.

Механизмы сжатия выглядят немного проще, их единственная цель – предоставить встроенное API, с помощью которого можно уменьшить объем данных (например, для уменьшения размера перемещаемых данных). API для этих целей находится в Windows.Storage.Compression (http://msdn.microsoft.com/library/windows/apps/windows.storage.compression.aspx), оно включает в себя классы Compressor и Decompressor, и тот и другой показаны в примере "Сжатие данных" (http://code.msdn.microsoft.com/windowsapps/Compression-sample-9d57900f). Хотя это API может использовать различные алгоритмы сжатия, включая алгоритм под названием MSZIP, оно не предоставляет средств для управления .ZIP-файлами и их содержимым. Для подобных целей вам понадобится либо использовать JavaScript-библиотеки сторонних производителей, либо написать WinRT-компонент на C# или VisualBasic, который может использовать API System.IO.Compression (смотрите Главу 5 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой").

Вадик Елетин
Вадик Елетин
Россия, г. Санкт-Петербург
Николай Жигульский
Николай Жигульский
Россия, Тверь, Тверской государственный технический университет