Опубликован: 10.12.2007 | Доступ: свободный | Студентов: 822 / 20 | Оценка: 5.00 / 5.00 | Длительность: 58:33:00
Лекция 16:

Объекты XPCOM

16.3.2. Потоки данных

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

Чтобы продемонстрировать эту гибкость, в листинге 16.6 показаны три способа создания потока. Этот поток используется для чтения локального файла (последовательности байтов).

var Cc = Components.classes;
var Ci = Components.interfaces;
var mode_bits = 0x01; // from nsIFileChannel
var perm_bits = 0; // from Unix/Posix open(2)
var file_bits = 0; // from nsIFileInputStream

var stream;
var file = ... // см. листинг 16.3 или 16.2

// [1] Непосредственное создание
stream = Cc["@mozilla.org/network/file-input-stream;1"];
stream = stream.createInstance(Ci.nsIFileInputStream);
stream.init(file, mode_bits, perm_bits, file_bits);

// [2] Создание на основе транспорта
var trans = Cc["@mozilla.org/network/stream-transport-service;1"];
trans = trans.getService(Ci.nsIStreamTransportService);
trans = trans.createInputTransport(stream,0,-1,true);
var stream2 = trans.openInputStream(0,-1,0);

// [3] Создание на основе канала
var channel = Cc["@mozilla.org/network/local-file-channel;1"]
channel = channel.createInstance(Ci.nsIFileChannel);
channel.init(file, mode_bits, perm_bits);
stream = channel.open();

// В любом случае, работа с потоком средствами JavaScript

var s2 = Cc["@mozilla.org/scriptableinputstream;1"];
s2 = s2.createInstance(Ci.nsIScriptableInputStream);
s2.init(stream);

var bytes = 100;
var content = null;
content = s2.read(bytes);
Листинг 16.6. Несколько способов создания потока

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

Пример 1. Файл читается или записывается непосредственно с использованием потока. Если не принять специальных мер, операции с файлом выполняются синхронно.

Пример 2. Это несколько необычный пример, поскольку в качестве отправной точки для создания потока используется поток. Объект- транспорт должен быть на чем-то основан, и в отсутствие сетевого протокола (соединения) единственной альтернативой является поток. Исходный поток используется для того, чтобы получить содержимое файла. Поток, возвращаемый транспортом (переменная stream2), отличается от исходного потока, поскольку транспорт заранее получает данные файла, чтобы передать их пользователю по очередному запросу. При работе же с исходным потоком (пример 1) чтение данных файла выполняется только тогда, когда пользователь обращается к потоку с запросом. Кроме того, транспорт автоматически закрывает "соединение" с файлом после того, как получены все данные файла.

Пример 3. Канал позволяет получить файл, не делая никаких предположений о механизме получения.

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

Достаточно небольших изменений в примерах 1 и 2, чтобы создать поток для записи вместо потока для чтения. В примере 3 такая замена невозможна, поскольку каналы применяются только для чтения. При записи данных предполагается, что поток вывода состоит из однобайтовых символов (расширенная кодировка ASCII). При выводе любого содержимого, состоящего из символов Unicode UTF16 (как, например, строки JavaScript), символы приводятся к однобайтовым – старший байт отбрасывается. Для вывода строк Unicode (как правило, они выводятся в кодировке UTF8) необходимо преобразование содержимого, которое обсуждается в следующем разделе.

16.3.2.1. Преобразование содержимого потоков

Внутри платформы все строки представлены с помощью Unicode. Простые файлы могут читаться как бинарные данные (для этого используется интерфейс nsIBinaryInputStream ), как восьмибитные символы (вариант по умолчанию) или как Unicode в соответствующей кодировке. Последний вариант имеет место при чтении файлов XML и их преобразовании в иерархию DOM, при получении данных из источника, который предоставляет информацию о кодировке (например, по протоколу HTTP), или при чтении DTD-файлов.

Для преобразования содержимого потока используется следующая пара XPCOM:

@mozilla.org/intl/scriptableunicodeconverter;1 nsIScriptableUnicodeConverter

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

@mozilla.org/streamconv;1?from={mime1}to={mime2}

Здесь mime1 и mime2 – типы MIME. Эти компоненты поддерживают интерфейс nsIStreamConverter. Такой объект получает входной поток и преобразует его содержимое, создавая при этом новый поток, из которого могут читаться преобразованные данные. Преобразования, поддерживаемые платформой, приведены в таблице 16.6. Подобный конвертер может быть реализован на чистом JavaScript.

Таблица 16.6. Преобразования потоков, поддерживаемые Mozilla
Исходный тип MIME Тип MIME после преобразования
application/http-index-format text/html
application/mac-binhex40 */*
application/x-unknown-content-type */*
compress несжатое содержимое
deflate несжатое содержимое
gzip несжатое содержимое
message/rfc822 application/vnd.mozilla.xul+xml
message/rfc822 */*
message/rfc822 text/html
multipart/byteranges */*
multipart/mixed */*
text/ftp-dir application/http-index-format
text/gopher-dir application/http-index-format
text/plain text/plain
x-compress несжатое содержимое
x-gzip несжатое содержимое

Изучив описание XPIDL объекта nsIStreamConverter, можно понять, каким образом такое преобразование может быть реализовано с использованием двух объектов nsIStreamListener вместо двух полноценных потоков. Такой подход позволяет конвертерам работать не только с потоками ввода, но и с потоками любого типа.

16.3.3. Транспорты

Объекты XPCOM транспортного уровня отвечают за передачу содержимого изнутри платформы Mozilla и наоборот. Таким образом, транспорты являются более общим средством, чем потоки, которые предназначены для передачи информации внутри платформы или операций чтения/записи с локальными файлами. Если потоки, как правило, выполняют синхронные операции и работают непосредственно с указанным источником, транспорты могут выполнять как синхронные, так и асинхронные операции. При этом они могут буферизовать данные в промежутках между запросами пользователя.

Транспортные уровни, доступные в настоящее время, приведены в таблице 16.7.

Таблица 16.7. Объекты XPCOM транспортного уровня.
Реализация Интерфейс
@mozilla.org/network/stream-transport-service;1 nsIStreamTransportService
@mozilla.org/network/socket-transport-service;1 nsISocketTransportService
@mozilla.org/network/storage-transport;1 nsITransport
@mozilla.org/xmlextras/soap/transport;1?protocol=http nsISOAPTransport
@mozilla.org/xmlextras/soap/transport;1?protocol=https nsISOAPTransport

Пять транспортов, приведенных в таблице, предназначены для: всех потоков, включая локальные файлы; сокетов; кэша браузера; транспорта HTTP для запросов SOAP; защищенного (SSL) транспорта HTTP для запросов SOAP.

Реализация stream-transport-service является относительно новой (с версии 1.3) и заменяет недоступную более реализацию file-transport-service. Вам могут встретиться примеры кода, использующие старую реализацию.

16.3.4. Каналы

Каналы представляют собой односторонний (только для чтения) механизм получения содержимого указанного URL. Хотя в принципе канал может использоваться и для чтения файлов, его основное назначение – работа c URL. Именно каналы выполняют значительную часть операций с содержимым при загрузке документа. Единственным каналом, который используется не для чтения, является специализированный канал для отправки форм, загрузки файлов на удаленный сервер или публикации Web-страниц.

В обычных ситуациях разработчик приложений редко создает каналы самостоятельно. Подобно объектам nsIFile и потокам, каналы чаще всего создаются платформой при выполнении операций более высокого уровня. Файл и поток образуют пару объектов, тесно связанных между собой, и аналогичную пару образуют канал и URL. Эта аналогия не идеальна, поскольку каналы создаются разработчиком вручную значительно реже, чем потоки. Как правило, работа с каналами скрыта в глубине того или иного протокола. Второе различие состоит в том, что канал представляет собой усовершенствованную реализацию запроса (объект nsIRequest ), который, в свою очередь, является модернизированным вариантом объекта-URL. Так что, строго говоря, канал и URL не являются независимыми объектами.

Как правило, работа с каналами начинается с обращения к следующей паре XPCOM:

@mozilla.org/network/io-service;1 nsIIOService

Этот компонент позволяет получить интерфейс nsIIOService при помощи метода getService(). Применительно к транспортам данный интерфейс фактически представляет собой службу имен для схем URL. Схема URL представляет собой первую группу символов полного URL (до двоеточия), которая указывает на протокол (метод доступа к ресурсу). Компонент принимает в качестве параметра имя схемы и возвращает программный объект для работы с соответствующим протоколом. Таким образом, интерфейс nsIIOService представляет собой отправную точку для получения содержимого определенного URL.

С помощью интерфейса nsIIOService можно создавать объекты с интерфейсами nsIURI и nsIURL. Каждый такой объект представляет определенный URL подобно тому, как объект с интерфейсом nsIFile представляет определенный файл. Указанные интерфейсы также предоставляют объекты для управления протоколом, соответствующим данной схеме или URL (protocol handlers). Именно эти объекты лежат в основе реализации каналов. Каждый из этих управляющих объектов поддерживает один или более типов каналов. После того, как в результате выбора пользователя или на основе простой строки получен объект, представляющий URL, с его помощью можно получить объект для управления соответствующим протоколом. В свою очередь, используя этот объект, можно получить объект, представляющий канал. Быстро выполнить все эти действия позволяет вызов метода newChannelFromURI() интерфейса nsIIOService.

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

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

Таблица 16.8. Каналы, поддерживаемые объектами XPCOM
Интерфейс канала Схема URL и/или идентификатор контракта
nsIChannel Все типы, перечисленные ниже
nsICachingChannel http:
nsIDataChannel data:
nsIEncodedChannel http:
nsIFileChannel file:
nsIFTPChannel ftp:
nsIHttpChannel http:
nsIImapMockChannel imap:
nsIInputStreamChannel @mozilla.org/network/input-stream-channel;1
nsIJarChannel jar:
nsIMultiPartChannel для внутреннего использования
nsIResumableChannel ftp: ( http: пока не поддерживается)
nsIUploadChannel file:, ftp:, http:
nsIViewSourceChannel view-source:
nsIWyciwygChannel wyciwyg: (не путать с 'wysiwyg' )

Как видно из таблицы 16.8, большинство каналов соответствуют схемам URL. Все каналы поддерживают базовую функциональность интерфейса nsIChannel, в первую очередь, – методы open() и asyncOpen(). Эти методы возвращают поток или объект – слушатель потока. Другие интерфейсы каналов лишь дополняют эту базовую функциональность с учетом специфики конкретных протоколов. Их не следует рассматривать как принципиально отличные каналы – скорее это расширения базового канала.

Несколько интерфейсов, приведенных в таблице, заслуживают особого упоминания. Так, nsIUploadChannel ориентирован на загрузку содержимого с локальной машины на удаленный сервер. Вместо того чтобы возвращать поток, он принимает его при инициализации и направляет данные потока на сервер. Канал nsIResumableChannel используется для загрузки по протоколу FTP, которая может приостанавливаться и возобновляться. Аналогичная функциональность для протокола HTTP в классическом браузере пока не поддерживается.

Еще один особый случай – канал для тривиальных "протоколов". Канал может использоваться не только для сложных сетевых протоколов, например FTP и HTTP, но и для простых случаев передачи данных между диском и памятью (чтение файла) или между различными структурами в памяти. Эти простые "протоколы" не подразумевают взаимодействия с URL, и разработчик может управлять ими вручную. Идентификатор контракта input-stream-channel, приведенный в таблице, связан как раз с таким использованием каналов.

Пример использования канала для доступа к локальному файлу приведен в листинге 16.6.