Журнал ВРМ World

Мировая история развития технологий управления эффективностью бизнеса – обзоры зарубежных публикаций

Передача файлов в 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)