Api функции для программ с проецированием файлов

Обновлено: 07.07.2024

Системное программирование в WINAPI, Марапулец Ю.В., 2019.

Целью предложенного пособия является систематизированное изложение принципов и приемов системного программирования в современных операционных системах в соответствии с рабочей программой дисциплины «Системное программирование» для студентов направления подготовки бакалавров 01.03.02 «Прикладная математика и информатика». В качестве базового языка использован язык программирования высокого уровня C++. В книге подробно рассмотрены основы построения базовых элементов современных операционных систем. Особое внимание уделено принципам разработки программ в операционных системах семейства Windows в среде WINAPI.
Учебное пособие предназначено для студентов, изучающих программирование, а также для самостоятельного изучения принципов программирования в среде WINAPI. Во втором издании приведены дополнительные знания, описывающие новые направления развития технологий системного программирования, устранены выявленные ошибки и опечатки.
Издание второе, исправленное и дополненное рекомендовано учебно-методическим советом ФГБОУ ВО «Камчатский государственный университет имени Витуса Беринга» в качестве учебного пособия для студентов, обучающихся по направлению подготовки «Прикладная математика и информатика».
Рекомендовано Дальневосточным региональным учебно-методическим центром (ДВ РУМЦ) в качестве учебного пособия для студентов направления подготовки бакалавров 01.03.02 «Прикладная математика и информатика» вузов региона.

Развитием СР/М явилась система СР/М-86. предназначенная для ПЭВМ класса XT. Дальнейшее совершенствование ОС СР/М привело к появлению многозадачной системы ССР/М-86. а затем и многопользовательской ОС МР/М-86. В рамках данного семейства ОС было создано большое число программ для ПЭВМ, включая трансляторы языков Бейсик. Паскаль. Си. Фортран. Кобол. Ада, а также текстовые редакторы. СУБД, графические пакеты и др.

Достоинство систем данного класса состояло в предельной простоте, малой потребной емкости ОЗУ для работы (20 Кбайт), а также возможности быстрой настойки на разные конфигурации ПЭВМ. Однако следует отметить, что представители семейства СР/М были довольно примитивны и имели слабый командный язык наряду с простейшей файловой системой. Поэтому на 16-разрядных ПЭВМ они нашли весьма ограниченное применение и дальнейшего развития не получили.

Оглавление.
Введение.
Глава 1 Принципы разработки программ в современных ОС.
§1.1. Операционные системы для ПЭВМ.
1.1.1. ОС СР/М.
1.1.2. OC DOS.
1.1.3. ОС OS/2.
1.1.4. ОС UNIX.
1.1.5. ОС LINUX.
1.1.6. ОС QNX.
1.1.7. ОС WINDOWS.
§1.2. Основы программирования в ОС Windows.
1.2.1. Принципы взаимодействия ОС Windows с прикладными программами.
1.2.2. Типы данных в Windows.
1.2.3. Графический и консольный интерфейсы.
1.2.4. Создание элементарного графического окна.
§1.3. Принципы разработки динамических библиотек.
1.3.1. Основные положения.
1.3.2. Главная функция DllМаіn().
1.3.3. Экспортирование функций из DLL.
1.3.4. Подключение DLL.
Глава 2 Разработка программного кода, учитывающего многозадачную архитектуру современных ОС.
§2.1. Общие принципы организации многозадачности.
2.1.1. Основные понятия и определения.
2.1.2. Планирование и диспетчеризация.
§2.2. Принципы разработки многопоточного приложения в ОС Windows.
2.2.1. Основы многозадачности и многопоточности в Windows.
2.2.2. API-функции для реализации механизма многопоточности.
2.2.3. Синхронизация потоков.
2.2.4. Использование классов MFC для создания потоков.
Глава 3 Принципы разработки программного кода для обработки прерываний и исключений.
§3.1. Система обработки прерываний.
§3.2. Общие принципы обработки исключений.
§3.3. Средства обработки исключений в Visual C++.
Глава 4 Принципы разработки программного кода,учитывающего организацию памяти в современных ОС.
§4.1. Основы организации памяти.
§4.2. Способы распределения памяти.
4.2.1. Простое непрерывное распределение памяти.
4.2.2. Распределение памяти с перекрытием (оверлейные структуры).
4.2.3. Распределение памяти разделами.
4.2.4. Сегментное распределение памяти.
4.2.5. Страничное распределение памяти.
4.2.6. Сегментно-страничное распределение памяти.
§4.3. Организация памяти в ОС Windows.
§4.4. Интерфейсы API-функций для разработки программ с выделением памяти в ОС Windows .
4.4.1. API-функции для программ с выделением виртуальной памяти.
4.4.2. API-функции для программ с проецированием файлов.
4.4.3. API-функции для программ с выделением динамических областей.
Глава 5 Принципы разработки программного кода для организации ввода/выводав современных ОС.
§5.1. Основы организации ввода / вывода в ПЭВМ.
§5.2. Общие принципы размещения данных на магнитных дисках.
§5.3. Принципы разработки программного кода для файлового ввода/вывода.
5.3.1. API-функции для организации ввода/вывода.
5.3.2. Механизмы асинхронного ввода/вывода.
Глава 6 Принципы разработки программного кода для работы с реестром ОС Windows.
§6.1. Структура и особенности реестра Windows.
§6.2. API-функции для работы с реестром Windows.
Глава 7 Принципы разработки программного кода для организации безопасности в ОС Windows.
§7.1. Технологии безопасности, реализованные в Windows.
§7.2. Создание структуры SECURITY_ATTRIBUTES.
§7.3. API-функции для обеспечения безопасности Windows.
Глава 8 Принципы разработки программного кода для обмена данными между процессами в ОС Windows.
§8.1. Обмен данными посредством буфера обмена Windows.
8.1.1. Структура и основные форматы буфера обмена.
8.1.2. Операции с буфером обмена.
8.1.3. Частные форматы буфера обмена.
§8.2. Обмен данными посредством каналов.
8.2.1. Общие положения и классификация каналов.
8.2.2. API-функции для работы с каналами.
§8.3. Обмен данными с использованием сокетов.
8.3.1. Общие положения. Виды сетевых протоколов.
8.3.2. API-функции для работы с сокетами.
§8.4. Обмен данными по технологии динамического обмена данными.
8.4.1. Общие положения.
8.4.2. API-функции библиотеки DDEML.
8.4.3. Механизмы обработки транзакций.
8.4.4. Завершение DDE-диалога.
8.4.5. Синхронные и асинхронные транзакции.
§8.5. Обмен данными по технологии связывания и внедрения объектов.
8.5.1. Общие положения.
8.5.2. Принципы разработки OLE-приложения.
Глава 9 Принципы разработки программного кода для обработки мультимедийной информации.
§9.1. Обзор мультимедийных устройств Windows.
§9.2. Элементарные API-функции для обработки звука.
§9.3. Принципы разработки программного кода для обработки формата RIFF.
9.3.1. Структура формата RIFF.
9.3.2. API-функции для обработки RIFF-файла.
§9.4. API-функции интерфейса DirectSound.
Список литературы.
Оглавление.

Первый шаг в отображении файла - это открытие файла при помощи вызова функции CreateFile . Чтобы гарантировать, что другие процессы не смогут писать в части файла, которая отображается, вам следует открыть файл с монопольным доступом. Кроме того, дескриптор файла должен оставаться открытым до тех пор, пока у процесса не исчезнет нужда в объекте "проецируемый файл". Легкий способ получить монопольный доступ состоит в том, чтобы установить нуль в параметре dwShareMode функции CreateFile. Дескриптор, возвращенный CreateFile используется функцией CreateFileMapping , чтобы создать объект "проецируемый файл".

Функция CreateFileMapping возвращает дескриптор объекта "проецируемый файл". Этот дескриптор должен использоваться при создании представления файла так, чтобы Вы могли получить доступ к совместно используемой памяти. Когда Вы вызываете CreateFileMapping, то задайте имя объекта, число байтов, которые отобразятся из файла и разрешение чтения - записи в отображаемой памяти. Первый процесс, который вызывает функцию CreateFileMapping, создает объект "проецируемый файл". Процессы, вызывающие CreateFileMapping для существующего объекта получают его дескриптор. Вы можете убедиться, успешно или нет завершился вызов CreateFileMapping, который создавал или открывал объект "проецируемый файл", при помощи вызова функция GetLastError . GetLastError возвращает значение NO_ERROR создающему процессу и ERROR_ALREADY_EXISTS последующим процессам.

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

  • Установите значения GENERIC_READ и GENERIC_WRITE в параметре fdwAccess функции CreateFile.
  • Установите значение PAGE_READWRITE в параметре fdwProtect функции CreateFileMapping.

Процедура создания объекта "проецируемый файл" не закрепляет физическую память, она только резервирует её.

Размер проецируемого в память файла

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

Параметры dwMaximumSizeHigh и dwMaximumSizeLow функции CreateFileMapping дают возможность устанавливать число байтов, которые будут отображаться из файла:

    Windows 95/98/Me: Параметр dwMaximumSizeHigh не используется, потому что эти операционные системы не поддерживаются 32-разрядной файловой системой. Это значение должно быть - нуль.

Если Вы не хотите изменять размер файла (например, когда отображаете файл только для чтения) вызовите функцию CreateFileMapping и установите оба её параметра dwMaximumSizeHigh и dwMaximumSizeLow в нуль. Выполнение этого приема создает объект "проецируемый файл" точно такого же размера, как и файл. В противном случае, Вы должны вычислить или приблизительно подсчитать размер законченного файла, потому что объекты "проецируемый файл" являются статическими по размеру; после создания их размер не может увеличиться или уменьшиться. Попытка проецировать файл с нулевой длиной этим способом завершается ошибкой с кодом ошибки ERROR_FILE_INVALID . Программы должны обнаруживать файлы с нулевой длиной и отвергать такие файлы:

Размер объекта "проецируемый файл" выбирается при помощи контроля, как далеко в файле с отображением в памяти Вы можете "видеть". Если Вы создаете 500 килобайтный объект "проецируемый файл" , Вы имеете доступ только к первым 500 КБ файла, независимо от размера его файла. Так как, чтобы создать больший объект "проецируемый файл", не требуется от Вас системных ресурсов, создайте его по размерам одинаковым с файлом (установив нули в обоих параметрах dwMaximumSizeHigh и dwMaximumSizeLow функции CreateFileMapping), даже если Вы не предполагаете просматривать весь файл. В затраты системных ресурсов входит создание представлений и доступа к ним.

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

Не менее мощным и гибким методом организации обмена данными между приложениями является метод, который базируется на проецируемых в память файлах (Files Mapping). Главная идея этого механизма основывается на использовании динамической разделяемой памяти системы для хранения в ней данных. Как известно, каждый процесс имеет свой участок памяти, называемый виртуальным адресным пространством. При использовании механизма проецируемых в память файлов данные становятся доступны из любого процесса, который использует этот файл. В этом случае говорят, что файл отображается в виртуальное адресное пространство процесса, поэтому данные, хранимые в файле, доступны процессу, который этот файл открыл. Механизм проецирования файлов в память используется, например, для исполняемых файлов приложений, а также для DLL.

Для работы с проецируемыми в память файлами существует целый ряд API-функций. Но прежде чем их рассматривать, разберемся в процессе организации обмена данными через проецируемые файлы. На первом этапе необходимо создать объект (файл, отображаемый в память), затем "отобразить" созданный объект в адресное пространство процесса приложения, получая возможность записи и чтения данных их этого файла. При отображении файла на определенный участок памяти (адресного пространства процесса) манипуляции с данными этого участка памяти отражаются на содержимом файла. После произведенных над объектом манипуляций необходимо закрыть доступ к данным файла (удалить проекцию и закрыть файл).

Рассмотрим некоторые функции для работы с проецируемым в память файлом. Для того чтобы создать объект файла, проецируемого в память, можно использовать функцию CreateFileMapping. Ее синтаксис выглядит следующим образом:

  • hFile - должен содержать дескриптор открытого файла, для которого будет создаваться объект, отображающий этот файл в память процесса.
  • lpFileMappingAttributes - указатель на структуру TSecurityAttributes. Если указателю присвоить nil, то атрибуты защиты устанавливаются по умолчанию.
  • flProtect - содержит флаги, которые задают режимы доступа к виду файла в памяти процесса. Этот параметр может принимать одно из следующих значений:
    • PAGE_READONLY — из вида файла можно только читать данные;
    • PAGE_READWRITE — разрешает чтение и запись данных в вид файла;
    • PAGE_WRITECOPY — разрешает чтение и запись данных в вид файла, но при записи создается новая копия вида файла.

    В случае успешного завершения эта функция возвращает дескриптор объекта (THandle), отображающего файл в память, а в случае неудачи — 0.

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

    • hFileMappingObject - должен содержать дескриптор объекта, отображающего файл в память, который был предварительно создан функцией CreateFileMapping.
    • dwDesiredAccess - задает режим доступа к виду файла и может принимать одно из следующих значений:
      • FILE_MAP_WRITE - чтение и запись в вид файла;
      • FILE_MAP_READ — только чтение из вида файла;
      • FILE_MAP_ALL_ACCESS — чтение и запись в вид файла;
      • FILE_MAP_COPY — при записи в вид файла создается его копия, а исходный файл не изменяется.

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

      Следующей функцией, противоположной по производимым действиям функции MapViewOfFile, является UnMapViewOfFile. Она отключает проецируемый файл от текущего процесса:

      Функция принимает указатель, возвращаемый MapViewOfFile, и использует его для отмены проекции файла на адресное пространство процесса. В случае успешной выгрузки функция возвращает True, в противном случае — False.

      И последняя функция, которую необходимо рассмотреть, — это CloseHandle. Она используется для закрытия дескриптора (многих системных объектов, а не только проекции файла).

      Как видно из синтаксиса функции, она принимает описатель объекта файлового отображения, полученный в результате выполнения функции CreateFileMapping и освобождает его. Для правильного завершения работы с объектом файлового отображения сначала следует применить функцию UnMapViewOfFile, а затем CloseHandle.

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

      Для демонстрации работы проецируемых в память файлов создадим приложение, которое будет записывать в такой файл строку и спустя некоторое время считывать ее оттуда. Для этого нам понадобится стандартный TextBox, кнопка, метка и таймер. Программа будет работать следующим образом: строка, записанная в поле редактора, после нажатия кнопки помещается в проецируемый файл. Далее, спустя некоторое время (задается таймером = 1 сек.), содержимое файла считывается и задается в качестве заголовка метки.


      В секцию описания переменных программы помещаем следующие объявления:

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

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

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

      Используемая литература: Программирование в Delphi. Трюки и эффекты. Александр Чиртик

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

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

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

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

      Для работы с проекциями файлов выполните следующие шаги:

      Создайте объект “файл”, вызовом функции CreateFile.

      Создайте объект “проекция файла”, вызовом функции CreateFileMapping. (Таблица 4).

      Закройте описатель файла, если он вам больше не нужен, вызовом CloseHandle.

      Отобразите весь или часть файла на адресное пространство, вызовом функции MapViewOfFile. (Таблица 5).

      Для отключения отображения воспользуйтесь UnmapViewOfFile. (Таблица 6).

      После окончания работы с проекцией файла закройте его описатель, вызовом функции CloseHandle.

      Таблица 1 - Функция CreateFileMapping

      HANDLE CreateFileMapping( HANDLE hFile, // Описатель файла LPSECURITY_ATTRIBUTES lpAttributes, // атрибут защиты DWORD flProtect, // защита страниц DWORD dwMaximumSizeHigh, // старшие 32 разряда размера файла DWORD dwMaximumSizeLow, // младшие 32 разряда размера файла LPCTSTR lpName // название объекта );

      HANDLE hFile - описатель файла для отображения.

      LPSECURITY_ATTRIBUTES lpAttributes - атрибут защиты. По умолчанию – NULL.

      DWORD flProtect - атрибут защиты страниц проекции файла: PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY.

      DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow - размер проекции файла. Для отображения всего файла передайте 0.

      LPCTSTR lpName - название объекта для разделения между процессами. Может быть NULL.

      Возвращаемое значение: Описатель объекта “проекция файла” в случае удачи. В случае ошибки – NULL.

      Таблица 2 - Функция MapViewOfFile

      LPVOID MapViewOfFile( HANDLE hFileMappingObject, // описатель объекта “проекция файла” DWORD dwDesiredAccess, // режим доступа к проекции DWORD dwFileOffsetHigh, // старшие 32 разряда смещения в файле DWORD dwFileOffsetLow, // младшие 32 разряда смещения в файле SIZE_T dwNumberOfBytesToMap // число байт файла для проецирования );

      HANDLE hFileMappingObject – описатель “проекция файла”

      DWORD dwDesiredAccess – запрашиваемый доступ к представлению: FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY.

      DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow – позиция в файле для начала проекции.

      dwNumberOfBytesToMap – число байт для проецирования из файла. 0 для проецирования всего файла.

      Возвращаемое значение: В случае удачи виртуальный адрес представления файла. NULL в случае ошибки.

      Таблица 3 - Функция UnmapViewOfFile

      BOOL UnmapViewOfFile( LPCVOID lpBaseAddress // Начальный адрес );

      Таблица 4 - Использование проецируемого в память файла

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

      Этот способ даже проще стандартного, рассмотренного выше. Во-первых, не нужно вызывать функцию CreateFile. Вы просто вызываете функцию CreateFileMapping и передаете значение INVALID_HANDLE_VALUE (или константу -1) в параметре hFile. Но при вызове функции CreateFileMapping следует передать в последнем ее параметре С-строку, содержащую имя этого объекта. Тогда другие процессы, если им понадобится доступ к разделяемой памяти, смогут вызвать функцию OpenFileMapping и передать ей то же самое имя. Учтите, что если разделяемая память используется в режиме записи данных более чем одним потоком, то вы должны предусмотреть синхронизацию работы этих потоков. Пример такой синхронизации при помощи объектов-событий рассматривается чуть ниже. Когда работа с объектом «проекция файла» завершена, процесс должен вызвать функцию CloseHandle. Как только все дескрипторы объекта будут закрыты, система освободит память, переданную из страничного файла.

      Описанную технологию можно проиллюстрировать следующим фрагментом кода:

      HANDLE hFile. hFileMapping:

      hFile = CreateFileC'File Name", . );

      / Работаем с файлом, как с массивом рАrrау /

      В этом примере «закрывающие» операции выполняются сразу после использования соответствующего дескриптора объекта. Это уменьшает вероятность утечки ресурсов.

      Таблица 5 - Функции для работы с файлами, проецируемыми в память

      munmap(token->location, token->nSize);close(token->nFileDes); remove(token->pFileName); free(token->pFileName);

      mmap — POSIX-совместимый (англ. Portable Operating System Interface for UnixПереносимый интерфейс операционных систем Unix) системный вызов Unix, позволяющий выполнить отображение файла или устройства на память. Является методом ввода/вывода через отображение файла на память и естественным образом реализует выделение страниц по запросу, поскольку изначально содержимое файла не читается с диска и не использует физическую память вообще. Реальное считывание с диска производится в «ленивом» режиме, то есть при осуществлении доступа к определённому месту.

      В Linux, Mac OS X и BSD mmap может создавать несколько типов отображений.

      Анонимные отображения — отображения пространства виртуальной памяти процесса, а не файла в пространстве файловой системы. По этой причине анонимное отображение схоже с функцией malloc и используется в некоторых реализациях malloc для определённых размещений. Следует заметить, что анонимные отображения не являются частью стандарта POSIX, хотя и реализованы почти во всех системах.

      Файловые отображения позволяют отобразить файл в виртуальной памяти. Доступ к этим участкам памяти приводит к чтению файла. Если отображения распределено между процессами, запись в это пространство в одном процессе окажет воздействие на другие процессы. Если используется частное (private) отображение, то изменения не будут видны другим процессам и не будут записаны в файл.

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

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

      К памяти, распределённой с помощью mmap, можно осуществлять доступ из дочерних процессов.

      mmap можно использовать для реализации межпроцессного взаимодействия (IPC). В современных операционных системах mmap обычно предпочтительней взаимодействию через распределённую память в System V.

      Основное различие между распределённой памятью System V (shmem) и вводом-выводом с распределением памяти (mmap) состоит в том, что распределённая память System V постоянна: не будучи явно удалены, данные будут храниться в памяти и оставаться доступными до тех пор, пока система не будет отключена. Память mmap не является постоянной между запусками приложения (только если отображение не зарезервировано в файле).


      В данной статье я хочу рассмотреть FileSystem API и File API, разобраться с его методами и показать пару полезных штук. Эта статья является компиляцией материалов с html5rocks (1, 2, 3). Все представленные ниже демки можно посмотреть по первым двум ссылкам. Третья ссылка так же предлагает ряд интересных демо. Ну а теперь займемся изучением материала.

      В качестве введения

      С помощью FileSystem API и File API веб приложение может создавать, читать, просматривать и записывать файлы находящиеся в области пользовательской «песочницы».

      • Чтение и управление файлами: File/Blob, FileList, FileReader
      • Создание и запись: BlobBuilder, FileWriter
      • Работа с директориями и права доступа: DirectoryReader, FileEntry/DirectoryEntry, LocalFileSystem
      1. File — собственно файл; позволяет получить такую доступную только для чтения информацию, как имя, размер, mimetype и прочее.
      2. FileList — «массив» объектов File.
      3. Blob — сущность, позволяющая разбирать файл по байтам.
      Поддержка браузерами и ограничение на хранение

      На момент написания статьи только Google Chrome 9+ имеет рабочую реализацию FileSystem API. И на данный момент пока нет никаких диалоговых окон для управления файлами и квотами на хранилище, поэтому нужно будет использовать флаг --unlimited-quota-for-files (в случае разработки приложений для Chrome Web Store будет достаточно манифеста с разрешением unlimitedStorage). Но все меняется и пользователи в скором времени получат возможность для управления правами по работе с файлами, которые будут требоваться приложению.

      Вам может потребоваться использование флага --allow-file-access-from-files , если вы дебажите приложение с использованием file://. Если этот флаг не использовать, то будут выброшены исключения типа SECURITY_ERR или QUOTA_EXCEEDED_ERR.

      Обращаемся к файловой системе

      Проверим поддержку браузером нужных нам функций

      Веб приложение может обратиться к файловой системе (естественно в ограниченной «песочнице») вызвав следующий метод window.requestFileSystem():

      Правила хранения, доступные значения window.TEMPORARY и window.PERSISTENT. данные, хранящиеся с использованием ключа TEMPORARY могут быть удаление по усмотрению браузером (например, если не хватает места). Если же выставлен ключPERSISTENT, то данные могут быть очищены только после действий пользователя или приложения.
      Размер (в байтах) хранилища, которое потребуется приложению.
      Callback-функция, выполняемая в случае успешного обращения к файловой системе. Ее аргументом является объект типа FileSystem.
      Необязательная callback-функция для обработки ошибок. Так же вызывается, когда возникают ошибки обращения к файловой системе. Параметром является объект типа FileError.

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

      Спецификация по FileSystem так же описывает API для синхронной работы, а именно интерфейс LocalFileSystemSync, который предполагается использовать совместно Web Workers. Но в этой статье данное API не будет рассмотрено.

      Возвращаясь к методу requestFileSystem() стоит описать возможные варианты возникающих ошибок:

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

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

      Для работы с файлам предусмотрен интерфейс FileEntry. Он обладает рядом методов и свойств, которые мы привыкли ассоциировать с обычными файлами. Приведем их ниже:

      Разберем на примерах азы работы с FileEntry.

      Создание файла

      Получить или создать файл можно с помощью метода getFile() у интерфейса DirectoryEntry. После обращения к хранилищу, callback возвращает нам объект FileSystem, содержащий в себе DirectoryEntry (fs.root), ссылающийся на корневую папку хранилища.

      Следующий код создаст пустой файл «log.txt»:

      Итак, после обращения к файловому хранилищу, у нас в руках оказывается FileSystem. Внутри сallback-функции мы можем обратится к методу fs.root.getFile(), передав имя файла, который требуется создать. Можно передать как относительный, так и абсолютный путь — главное чтобы он был верным. Например, ошибочным будет создание файла, если его родительская папка не существует. Вторым аргументом метода getFile() является объект, описывающий параметры объекта, которые будут к нему применены, если он еще не создан. Более подробно можно прочитать в документации.

      Чтение файла по имени

      Следующий код обращается к файлу «log.txt» и читает его содержимое с помощью FileReader API, после чего записывает все содержимое в блок <textarea>. Если файл не существует, то будет выброшена ошибка.

      • FileReader.readAsBinaryString(Blob|File) — результат будет содержать байтовую строку.
      • FileReader.readAsText(Blob|File, opt_encoding) — результат будет содержать текстовую строку. Кодировка по умолчанию — 'UTF-8', менять можно с помощью задания опционального параметра
      • FileReader.readAsDataURL(Blob|File) — на выходе имеем data URL.
      • FileReader.readAsArrayBuffer(Blob|File) — получаем данные в виде ArrayBuffer.

      Иногда нам может потребоваться не весь файл, а лишь его часть, для этого удобно использовать File.slice(start_byte,length).
      Выглядит это так:

      В следующем примере мы сможем прочитать либо нужные на байты, либо весь файл целиком. Особое внимание обратите на onloadend и evt.target.readyState, которые в данном случае будут заменять нам событие onload. (О событиях чуть ниже).

      • onloadstart
      • onprogress
      • onload,
      • onabort
      • onerror
      • onloadend
      Запись в файл

      С помощью следующего кода мы создадим файл «log.txt» (если он не существует) и запишем в него 'Ipsum Lorem'.

      Как видно, мы обращаемся к методу createWriter() для получения объекта. Кроме этого, мы обрабатываем события окончания записи в файл и возможного создания ошибки.

      Дописываем данные в файл

      Следующий код допишет 'Hello World' в конец файла. Если файла нет, то выброситься ошибка.

      Создание копий выбранных файлов

      Следующий код позволяет пользователю выбирать несколько файлов, используя <input type=«file» multiple> и создает копии этих файлов.


      Для упрощения выбора файлов можно использовать HTML5 Drag and Drop.

      Удаление файлов

      Следующий код удалит 'log.txt'.

      Работа с директориями

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

      Создание директорий

      Для создания и обращения к директориям используется getDirectory() интерфейса DirectoryEntry. Можно передавать как имя, так и путь до директории.

      Создадим в корне директорию «MyPictures»:

      Поддиректории

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

      Теперь у нас создана директория «music/genres/jazz» и мы можем обращаться к любому ее уровню и создавать в них новые файлы. Например:

      Разбираем содержимое директории

      Чтобы узнать, что содержится в директории нужно создать DirectoryReader и вызвать его метод readEntries(), но при этом нет гарантии, что вернется все содержимое выбранной директории (. ). Это значит, что надо обращаться к методу DirectoryReader.readEntries(), пока результат не станет пустым. Пример:

      Удаляем директорию

      Для удаления следует вызвать метод DirectoryEntry.remove(). Важно знать, что если попытаться удалить не пустую директорию, то будет выброшена ошибка.

      Удалим пустую директорию «jazz» из "/music/genres/":

      Рекурсивно удаляем директории

      Если у вас есть не пустая директория и вы все же хотите ее удалить, то вам поможет метод removeRecursively(), который удалить и директорию и все ее содержимое.

      Произведем эту операцию с директорией «music»:

      Копируем, переименовываем и перемещаем

      FileEntry и DirectoryEntry полностью идентичны в этом аспекте.

      Копируем

      И FileEntry и DirectoryEntry имеют метод copyTo() для копирования. В случае директорий, метод рекурсивно создаст и все содержимое.

      Скопируем «me.jpg» из одной директории в другую:

      Перемещение или переименование

      У FileEntry и DirectoryEntry есть метод moveTo(), позволяющие перемещать и переименовывать файлы и директории. Первым аргументом является родительская папка, вторым (опциональным) параметром является новое имя.

      Переименуем «me.jpg» в «you.jpg»:


      Переместим «me.jpg» из корневой директории в «newfolder».

      Use Cases

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

      Читайте также: