- 13 февраля 2004 г.
Передача файлов в Web-сервис
Во второй статье рубрики рассматриваются два основных подхода, используемых
для передачи бинарных данных в Web-сервис.
SOAP бинарные данные
В этой статье ведущий колонки «Совет» (Tip) рассматривает различные подходы, используемые для передачи бинарных данных (обычно файлов) Web-сервис.
В своем развитии протоколы Web-сервисов прошли долгий путь, начиная поддержкой очень простых запросов с несложными параметрами и заканчивая полной поддержкой современных объектно-ориентированных языков. Спецификация XML-RPC (Удаленный вызов процедуры на XML), являющаяся, пожалуй, одной из самых ранних форм Web-сервисов, поддерживала только простые типы – строки, целые числа, логические выражения и т.п. Следующим шагом явилось появление в SOAP правил кодирования для объектов. Наконец, последний шаг – улучшение бинарного кода - было введено в документе консорциума W3C "SOAP с вложениями" (SOAP with attachments).
Первоначально "SOAP с вложениями" был предложен в качестве расширения SOAP 1.1, и сегодня эта технология поддерживается в большинстве инструментальных средств SOAP. Несмотря на то, что вложения пока не поддерживаются в официальной версии спецификации W3C SOAP 1.2, в настоящий момент ведутся работы с целью их включения в этот документ в (умозрительно) ближайшем будущем.
Web-сервис и бинарные данные
Автор не испытывает ни малейших сомнений в том, что успех XML для решения задач интеграции приложений связан с использованием текстового кодирования (как противопоставления бинарным протоколам, как, например, CORBA, объектно-ориентированному стандарту RPC, или RMI, основанному на Java стандарту RPC). Текстовое кодирование предпочтительно по нескольким причинам, наиболее значимая из которых заключается в том, что в случае необходимости упрощается отладка и развертывание конкретного приложении.
И все же использование текстового кодирования имеет и свои «темные стороны», а XML не располагает действенным способом включения бинарных данных. Согласно рекомендации консорциума W3C «XML-схема» (W3C XML Schema), бинарные данные должен быть закодированы с использованием кода Base-64 или шестнадцатеричного кода. К сожалению, размер данных, закодированных с помощью шестибитного кода (Base-64), оказывается в полтора раза больше по сравнению с размером незакодированных данных. Размер данных, преобразованных в систему счисления с основанием 16, оказывается в два раза больше исходного. Такие накладные расходы приемлемы в случае небольших фрагментов бинарных данных, но для больших наборов данных такой подход неприемлем.
Вместе с тем бинарные данные удобны для многих приложений, например:
- Для систем обеспечения безопасности необходимы ключи, знаки "решетка", сертификаты, сами закодированные данные.
- Мультимедийные приложения используют фото, музыку и фильмы.
- В некоторых приложениях применение XML-представления оказывается неэффективным, например, в случае автоматизированного проектирования (CAD) или автоматизированного производства (CAM).
- Существуют тысячи файловых форматов, которые появились до XML – форматы текстовых процессоров, электронных таблиц, шрифтов, векторной графики и т.д.
Хотя создание XML-версий этих файловых форматов возможно (аналогично формату SVG (Scalable Vector Graphics, масштабируемая векторная графика) для векторной графики), бинарные данные существуют уже давно и вряд ли потеряют популярность.
Наконец, существуют проблемы и с самим XML! Например, включение одного XML-документа в другой XML-документ не является тривиальной задачей (синтаксически корректное решение опирается на разделы CDATA и переход на другие символы).
MIME и код base 64Чтобы избежать часто возникающие недоразумения, подчеркнем, что MIME не требует обязательного использования кодирования base 64. Точнее реализации HTTP не кодируют включения; их кодируют только почтовые клиенты, чтобы обойти ограничения SMTP (поэтому при сравнении с XML каких-либо преимущества отсутствуют). |
Поэтому, чтобы реализовать требования всех этих приложений, Web-сервисы должны эффективно поддерживать бинарные данные. Предлагаемое решение – SOAP с вложениями, который, если попытаться сказать кратко, удаляет бинарную информацию из полезной нагрузки XML и сохраняет ее непосредственно в запросе HTTP в качестве содержания MIME multipart/related.
Таким образом, при проектировании Web-сервиса, который использует бинарные данные, следует воспользоваться один из следующих подходов:
- Если набор данных невелик, можно использовать код Base-64 для полезной нагрузки XML; накладные расходы для небольших фрагментов данных не являются серьезной проблемой.
- Если набор данных значителен, включение – единственный практически разумный выбор.
Ниже приведен Листинг 1 (запрос SOAP с параметром, закодированным с помощью шестибитного кода, в котором особого внимания заслуживает элемент address.
Листинг 1. Параметр в системе счисления с основанием 62
POST /ws/retrieve HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml multipart/related, text/*
Host: localhost:8080
SOAPAction: ""
Content-Length: 540
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ps:retrieve
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ps="http://psol.com/2004/ws/retrieve">
<address xsi:type="xsd:base64Binary">d3d3Lm1hcmNoYWwuY29t</address>
</ps:retrieve>
</soapenv:Body>
</soapenv:Envelope>
Реализация включений
Разработчики Java могут воспользоваться включениями, используя JAX-RPC (API на Java для RPC, основанного на XML) и SAAJ (API SOAP с включениями для Java). Читателю следует обратить внимание на аббревиатуру SAAJ: JAX-RPC поддерживает включения (см., например, Ресурсы). Различие между JAX-RPC и SAAJ заключается в уровне абстракции, а не в функциональных возможностях.
JAX-RPC – это API высокого уровня, он более абстрактен по сравнению с SAAJ. JAX-RPC скрывает за слоем протокола RMI (Remote Method Invocation, технология построения распределенных приложений в спецификации языка Java) большинство ориентированных на протокол аспектов SOAP. Разработчик занимается объектами Java, а препроцессор превращает их в узлы SOAP. Для представления включений JAX-RPC использует классы java.awt.Image и javax.activation.DataHandler.
SAAJ более близок к протоколу. Поскольку для создания SOAP-сообщений с помощью SAAJ требуется больше усилий, чем с JAX-RPC (и, следовательно, он не предоставляет автоматических связей с WSDL), в большинстве случае читатель отдаст предпочтение JAX-RPC. Вместе с тем, низкоуровневые аспекты более удобны для иллюстрации того, как на самом деле работают включения. Листинг 2 – это запрос SOAP с включением. Этот запрос просит сервер изменить размер фотографии; поскольку размер файла фотографии велик, использование включения более предпочтительно.
Листинг 2. Параметр включения
POST /ws/resize HTTP/1.0
Content-Type: multipart/related; type="text/xml";
start="<EB6FC7EDE9EF4E510F641C481A9FF1F3>";
boundary="----=_Part_0_7145370.1075485514903"
Accept: application/soap+xml, multipart/related, text/*
Host: localhost:8080
SOAPAction: ""
Content-Length: 1506005
------=_Part_0_7145370.1075485514903
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <EB6FC7EDE9EF4E510F641C481A9FF1F3>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ps:resize
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ps="http://psol.com/2004/ws/resize"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<source href="cid:E1A97E9D40359F85CA19D1B8A7C52AA3"/>
<percent>20</percent>
</ps:resize>
</soapenv:Body>
</soapenv:Envelope>
------=_Part_0_7145370.1075485514903
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-Id: <E1A97E9D40359F85CA19D1B8A7C52AA3>
note: binary data deleted...
------=_Part_0_7145370.1075485514903--
Приведенный ниже Листинг 3 демонстрирует создание запроса SOAP. Этот запрос просит сервер изменить размер изображения. Для реализации этой процедуры необходимо выполнить следующую последовательность действий:
- Создать соединение SOAP и объекты сообщения SOAP через фабрики (factories).
- Извлечь тело сообщения из объекта сообщения (промежуточный шаг: извлечь часть (part) SOAP и конверт).
- Создать новый XML-элемент для представления запроса и установить стиль кодирования.
- Создать включение и инициализировать его с объектом DataHandler.
- Создать еще элементы для представления двух параметров (source и percent).
- Ассоциировать включение с первым параметром посредством добавления атрибута href. Ссылка на это включение осуществляется через универсальный локатор ресурса cid (content-id).
- Задать значение второго параметра непосредственно в виде текста и вызвать сервис.
Сервис предоставляет ответ с изображением, размеры которого изменены, причем это изображение представлено в виде включения. Чтобы извлечь его, можно протестировать SOAP на предмет неисправности (т.е. ошибки). Если неисправностей нет, включение следует извлечь в виде файла, а затем обработать.
Листинг 3. Использование SAAJ
public File resize(String endPoint,File file)
{
SOAPConnection connection =
SOAPConnectionFactory.newInstance().createConnection();
SOAPMessage message = MessageFactory.newInstance().createMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();
SOAPBodyElement operation =
body.addBodyElement(
envelope.createName("resize",
"ps",
"http://psol.com/2004/ws/resize"));
operation.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
DataHandler dh = new DataHandler(new FileDataSource(file));
AttachmentPart attachment = message.createAttachmentPart(dh);
SOAPElement source = operation.addChildElement("source",""),
percent = operation.addChildElement("percent","");
message.addAttachmentPart(attachment);
source.addAttribute(envelope.createName("href"),
"cid:" + attachment.getContentId());
width.addTextNode("20");
SOAPMessage result = connection.call(message,endPoint);
part = result.getSOAPPart();
envelope = part.getEnvelope();
body = envelope.getBody();
if(!body.hasFault())
{
Iterator iterator = result.getAttachments();
if(iterator.hasNext())
{
dh = ((AttachmentPart)iterator.next()).getDataHandler();
String fname = dh.getName();
if(null != fname)
return new File(fname);
}
}
return null;
}
Стоит обратить внимание на то, что в Листинге 3 включение находится вне XML-сообщения. Такой подход повышает производительность.
Говоря о производительности, рассмотрим Листинг 4, в котором показана более общая (и существенно более короткая) версия JAX-RPC Листинга 3. Прекомпилятор JAX-RPC генерирует переходник (stub), которая значительно упрощает кодирование. В этом случае в качестве параметра метода передается объект DataHandler, а JAX-RPC автоматически генерирует это включение.
Листинг 4. Более эффективный JAX-RPC
public File resize(File file)
throws ServiceException, RemoteException
{
AttachmentService service = new AttachmentServiceLocator();
AttachmentTip port = service.getAttachmentTip(); // get stub
DataHandler dh = new DataHandler(new FileDataSource(file));
DataHandler result = port.resize(dh,20);
return new File(result.getName());
}
Заключение
Возможность выбора всегда желательна и SOAP предоставляет такую возможность при работе с бинарными данными: их можно либо кодировать с помощью кода base 64 внутри полезной нагрузки XML, что является хорошим решением для небольших наборов данных, либо присоединять более крупные бинарные незакодированные файлы к этому запросу.
Ресурсы
- Форум, посвященный обсуждению статей в колонке Беноита Марчала (Benoît Marchal) “XML в действии” (Working XML).
- Для изучения SAAJ и JAX-RPC можно скачать Axis. Необходимо отметить, что для работы с включениями читателю потребуется установить два дополнительных компонента: JavaMail и JavaBeans Activation Framework. Автор хотел бы подчеркнуть, что включения не работают в Axos (версии 1.1 и 1.2), если эти две компоненты не установлены.
- Международный консорциум W3C ведет работы над поддержкой вложений в SOAP 1.2 (attachment support to SOAP 1.2).
- Статья Рассела Бутека (Russell Butek) «Отправка и получение сообщений SOAP с JAX-RPC» ("Send and receive SOAP messages with JAX-RPC") (developerWorks, сентябрь 2003 г.).
- В своей статье «Преобразование типов Apache SOAP» (Apache SOAP type mapping) Гевин Бонг (Gavin Bong) рассматривает соответствие между SOAP и технологией Java. Несмотря на то, что этот материал написан с позиции Apache SOAP, он применим и к большинству инструментальных средств SOAP (developerWorks, апрель 2002 г.).
- Ранняя версия (6.0) сервера приложений IBM WebSphere (IBM WebSphere Application Server Technology for Developers, version 6.0,), поддерживающего последнюю редакцию спецификации J2EE (версию 1.4).
- Множество других ресурсов в зоне XML рубрики developerWorks. Полный список статей колонки «Совет» (Tip) приведен на странице с кратким описанием этой рубрики.
- На странице информационный бюллетень Web-cервисов/Советов по XMLможно подписаться на еженедельную рассылку новостей рубрики developerWorks.
- Книга Беноита Марчала «Прикладные решения XML» (Applied XML Solutions) можно приобрести в книжном магазине разработчика рубрики developerWorks.
- На странице IBM Certified Developer in XML and related technologies приведена информация о том, как стать сертифицированным разработчиком XML.
Об авторе
Бёнуа Маршаль (Benoît Marchal) – бельгийский консультант. Он автор многих книг, посвященных XML, в том числе «XML на примерах, второе издание» (XML by Example, Second Edition). Беноит Марчал специализируется в области XML, технологий Java и электронной коммерции. Более подробную информацию о нем можно получить на его сайте www.marchal.com. С ним можно связаться по адресу bmarchal@pineapplesoft.com. |
Автор: Бёнуа Маршаль (Benoit Marchal)