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

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

Возможность передачи файлов на Web-сервер предусмотрена протоколом RFC1867 с названием Form-based file Upload in HTML (типовой файл на HTML для переписи на Web-сервер). Полный текст протокола вы найдете на сервере http://www.rfc-editor.org в разделе Request for Comments.

В соответствии с протоколом RFC1867 вы размещаете в документе HTML форму, содержащую тег . Для этого тега указывается параметр TYPE со значением FILE, например:

Select Uploaded File:


 

При этом в параметре ENCTYPE тега

следует указать формат передаваемых данных как multipart/form-data (вместо применяемого по умолчанию формата appliation/x-www-form-urlencoded). Кроме того, в поле ACTION необходимо задать адрес расширения сервера Web, способного соответствующим образом принять и обработать данные формата multipart/form-data.


Рис. 1. Кнопка для выбора локального файла

Когда определенная таким образом форма загружается в окно браузера, рядом с полем ввода тега появляется кнопка Browse (рис. 1).



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

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

После выбора файла путь к нему появляется в поле редактирования текста тега (рис. 1). Теперь вы можете нажать кнопку типа submit, и содержимое указанного локального файла вместе с данными других полей формы будет отправлено на сервер Web для обработки расширением, заданным в поле ACTION.

По результатам обработки расширение сервера Web динамически сформирует документ HTML и вернет его посетителю.



Рис. 3. Добавление файлов вложений к почтовому сообщению на сервере HotMail

На сегодня нам известно несколько практических реализаций этого протокола RFC1867. Например, технология удаленной загрузки файлов применяется в системах бесплатной электронной почты, таких как HotMail или NetAddress.

Как известно, классическая электронная почта допускает возможность передачи вместе с текстовым сообщением дополнительных файлов вложений (attachments). Системы бесплатной электронной почты обычно создаются на базе серверов Web, причем для работы с почтовыми сообщениями пользователи применяют не специальные почтовые программы, а браузеры. Что же касается файлов вложений, то их предлагается добавлять через упомянутый выше протокол RFC1867. На рис. 3 показана соответствующая страница сервера HotMail. Здесь вам предлагается вначале выбрать файл, нажав кнопку Browse, а затем добавить его к сообщению при помощи кнопки Attach to Message. Аналогичная технология применяется и на сервере NetAddress.

Загрузка файлов пользователя через протокол RFC1867 применяется на серверах http://www.geocities.com и http://www.spree.com для формирования личных домашних страниц (рис. 4). Здесь допускается указывать сразу несколько файлов, которые будут загружены на сервер с вашего локального диска.



Рис. 4. Загрузка файлов домашних страниц на сервере http://www.geocities.com

И наконец, протокол RFC1867 используется в системе удаленной антивирусной проверки файлов посетителей сервера АО "ДиалогНаука".

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

Заметим, что первоначально технология загрузки файлов через протокол RFC1867 была доступна только пользователям Netscape Navigator начиная с версии 2.0. В Microsoft Internet Explorer такая возможность появилась только в версии 4.0. Тем не менее на сервере http://www.microsoft.com вы можете загрузить файл обновления и для старых браузеров фирмы Microsoft, позволяющий работать с указанным протоколом.

Для загрузки файла обновления зайдите на страницу http://www.microsoft.com/msdownload и найдите там раздел Windows Update. Выберите в области Internet Explorer строку Windows 95 and Windows NT 4.0. Затем найдите раздел Add-on Windows Internet Features for Internet Explorer 3.0 и загрузите соответствующий документ HTML. Далее выберите в списке строку Internet Explorer 3.02 File Upload Add-On for Windows 95 or NT 4.0. Нажав кнопку загрузки, вы получите файл с именем rfc1867.exe.

Заметим, что состав и расположение страниц сервера Microsoft изменяются достаточно часто, поэтому, если приведенный выше алгоритм не сработал, попробуйте провести поиск по ключевым словам rfc1867.exe или File Upload Add-On.

Реализация расширения сервера Web

Рассмотрим реализацию удаленной загрузки на примере расширения ISAPI для сервера Microsoft Internet Information Server более подробно.

Основная функция расширения, обрабатывающего формы с применением протокола RFC1867, - сканирование данных формата multipart/form-data. Расширение должно быть способно принять данные этого формата от удаленного пользователя, выделить содержимое полей формы и переданных файлов, сохранить файлы на диске и затем передать их тому или иному программному модулю. По результатам обработки, выполненной этим модулем, расширение должно подготовить документ HTML и отправить его обратно пользователю.

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

Прием данных от браузера

Расширения серверов Web, принимающие данные от обычных форм (не содержащих ссылок на локальные файлы), чаще всего имеют дело с небольшими объемами информации. Как правило, это всего несколько строк текста.

Если общий объем передаваемых данных не превышает 48 Кбайт, то расширение ISAPI автоматически считывает все данные пользователя. Функция HttpExtensionProc получает ссылку на структуру данных типа EXTENSION_CONTROL_BLOCK, причем в поле lpbData этой структуры имеется указатель на область принятых данных. Поле cbAvailable содержит размер этой области, а поле cbTotalBytes - общий размер принятых данных.

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

Таким образом, автоматически принимается только часть данных, отправленных браузером. Остальные данные необходимо дочитать функцией ReadClient - указатель на нее передается в одноименном поле структуры EXTENSION_CONTROL_BLOCK.

Работа с функцией ReadClient имеет свои особенности.

Через последний параметр функции ReadClient необходимо передать указатель на слово DWORD, содержащее размер принимаемых данных. Вы можете указать здесь значение (cbTotalBytes - cbAvailable), так как функция ReadClient не будет читать предварительно прочитанные данные, адрес которых находится в поле lpbData. После того как функция возвратит управление, в указанное слово будет записано количество действительно прочитанных байтов данных.

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

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

Сканирование принятых данных формата multipart/form-data

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

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

<Разделитель><
>
Content-Disposition: form-data;
 name="<Имя поля>"<
>
<
>
<Данные поля><
>
. . .

Если блок содержит файл, он имеет следующий формат:

<Разделитель><
>
Content-Disposition: form-data;
 name="<Имя поля>"; 
filename="<Путь к файлу>"<
>
<
>
. . . .
<Тело файла>
. . . .
<
>
<Разделитель><
>

Браузер выбирает разделитель таким образом, чтобы использованная в нем последовательность символов не встречалась в посылаемом содержимом полей и файлов. Обычно он начинается с нескольких символов "-", за которыми следует достаточно большое шестнадцатеричное число.

На следующей после разделителя строке располагаются поля Content-Disposition, name и filename. Поле Content-Disposition имеет значение form-data, оно указывает, что далее будут располагаться данные, извлеченные из поля формы. Соответствующее имя находится в поле name и заключено в кавычки.

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

Вот пример данных, извлеченных из формы с двумя полями.

---------------7ce34234303cc<
>
Content-Disposition: form-data;
 name="text1"<
>
<
>
Sample of text1<
>
---------------7ce34234303cc<
>
Content-Disposition: form-data;
 name="fupload";
 filename="c:utafd.exe"<
>
<
>
. . . .
<Тело файла>
. . . .
<
>
---------------7ce34234303cc<
>

Первое поле называется text1. Оно содержит строку Sample of text1. Имя второго поля - fupload. Оно содержит файл, имеющий путь c:utafd.exe. Это путь, указанный пользователем для своего локального диска при выборе файла в диалоговой панели (рис. 2).

Далее идет пустая строка и тело файла. Файл заканчивается символами возврата каретки, перевода строки и разделителем.

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

Обработка принятых файлов

Организуя в расширении ISAPI сервера Web обработку файлов, принятых с использованием протокола RFC1867, нужно помнить, что:

  • расширение реализуется в виде DLL- библиотек;
  • оно работает в многопоточном режиме и может быть запущено одновременно несколькими посетителями. Поэтому следует предусмотреть средства синхронизации доступа к критическим ресурсам, таким как файлы;
  • код расширения и данные находятся в адресном пространстве процесса, созданного для сервера Web. Ошибки, возникающие при работе расширения, могут привести к полному нарушению работоспособности сервера. Поэтому, если при обработке принятых файлов возникнут различные непредвиденные ситуации, а также в процессе отладки системы имеет смысл запускать программу обработки файлов как отдельный процесс. Аварийное завершение такого процесса не скажется на работоспособности сервера Web.

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

Другой важный момент связан с временем загрузки и обработки принятого файла (или файлов).

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

Приложение FUPLOAD



Рис. 5. Форма для работы с расширением FUPLOAD

Теперь перейдем от теории к практике. Мы рассмотрим исходный текст расширения ISAPI с названием FUPLOAD, созданное нами для загрузки файлов по протоколу RFC1867 на сервер Microsoft Internet Information Server.

Наше приложение работает вместе с формой, внешний вид которой показан на рис. 5.

В этой форме вы можете ввести данные о пользователе (идентификатор и пароль), текстовое описание передаваемого файла, а также указать действия, выполняемые с файлом. При помощи кнопки Browse выбирается передаваемый файл, а при помощи кнопки Send File - содержимое формы (и выбранного файла) передается приложению FUPLOAD.

Получив запрос на обработку формы, программа FUPLOAD принимает данные от браузера посетителя и сканирует их. Результат сканирования отображается в динамически создаваемом документе HTML (рис. 6).

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



Рис. 6. Документ HTML с результатами обработки формы

Финальный фрагмент документа HTML (рис. 6) формирует функция processData. Ей передается путь к упомянутому выше временному файлу. Наш вариант функции не выполняет никакой обработки файла, однако ее можно изменить соответствующим образом, подключив модуль проверки правописания, переводчик или антивирусную программу.

Форма для вызова расширения FUPLOAD

В листинге 1 мы приводим сокращенный исходный текст формы, выполняющей вызов расширения ISAPI (библиотеки DLL с именем fileupl.dll).

ЛИСТИНГ 1 Сокращенный исходный текст документа HTML с формой

. . .
. . .
Выберите отправляемый файл



 

Наиболее интересные фрагменты, имеющие отношение к загрузке файла, мы выделили жирным шрифтом. Обратите внимание на поля ENCTYPE, METHOD и ACTION тега

, а также на поля тега .

(Окончание в следующем номере.)

ОБ АВТОРАХ

Братья Александр Вячеславович и Григорий Вячеславович Фроловы - авторы серий книг "Библиотека системного программиста" и "Персональный компьютер. Шаг за шагом". E-mail: frolov@glasnet.ru Web: http://www.glasnet.ru/~frolov