login button

Five Easy Steps to Send and Receive Binary Attachments Using WSF/PHP

Story :

Level : Realm : Project :

Sending and receiving binary attachments using WSF/PHP can be done in five easy steps. To send an attachment you need to have a sending entity (a client or a server), a payload that meets the requirement of the receiving entity, binary data content, a mechanism to send the payload over the wire, and a receiving entity (a server or a client) that is ready to accept and process binary data. Danushka Menikkumbura explains..

 

Table of Contents

Introduction
Who should read this article?

Step 1 : Creating a Payload
    1.1. Operation Name
    1.2. Attachment Node
Step 2 : Creating a  Client/Server
Step 3 : Binding Binary Data Content to the Payload
Step 4 : Sending a Message with Attachments
Step 5 : Processing Received Attachments

Using Sample Application
Summary


Introduction

Since the implementation of the very first Web application, sending binary attachments has been getting a lot of attention. A Web application, be it an e-mail application, online shopping application, stock broker etc., is not very useful if it does not support sending and receiving binary attachments. It is quite disappointing to observe that most of the Web services frameworks do not support sending attachments even though it is a must have feature. When it comes to PHP Web services, the situation is the same. In that perspective, Web Services Framework for PHP (WSF/PHP) comes in handy as you can use it to send or receive binary attachments by following a couple of easy steps.
In this article, you will learn how to send and receive binary attachments using WSF/PHP in just five steps. I will also be giving detailed explanations where necessary, so that by the end of this article you will be competent in sending and receiving binary attachments with WSF/PHP.
To make the learning experience more practical and to understand the concepts clearly, we will be developing a simple client/server application. This application is claimed to be used in an Art Gallery to select photos sent by freelance artists and send them to their printing department for printing.
   
Who should read this article?

There will be two kinds of programmers who will read this article: experienced programmers who want to know how to use WSF/PHP to send and receive binary attachments, and novices to Web programming with limited knowledge about SOAP, XML, binary optimized packaging, etc, but want to send and receive binary attachments in their applications. I will be mainly focusing on the latter so that they will gain enough confidence to send and receive attachments using WSF/PHP.



Step 1 : Creating a Payload


Before we create the required payload, we need to know what is meant by 'payload'.  According to The New American Webster Dictionary, the meaning of the word payload is as follows.
        "the weight a vehicle carries in freight or paying passengers".
However, the payload that we are talking about is just an XML stream. SOAP is the information exchange protocol and HTTP is the transport that we use.  XML is a simple markup language that allows you to express a piece of information in a tree structure [1]. SOAP is the mechanism we use to exchange information (payloads) between peers, and the payload is carried inside the body of the SOAP envelop [2]. HTTP is the transport that carries SOAP messages between peers [3]. The payload and the SOAP envelop is analogous to the letter and the envelop combination you are familiar with. The SOAP envelop and the HTTP relationship is analogous to the letter, envelop and the postman relationship.
The payload carries information that the client wants to send to the server and vice-versa. Now that you have an idea of what payload is, let's see what are the elements that are required in a payload to make it eligible to carry an attachment. The following can be considered as a blueprint of a payload that can be used to send an attachment.

<ns-prefix:operation-name xmlns:ns-prefix="namespace-uri">
    
<
ns-prefix:attachment-node-name xmlmime:contentType="content-type" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
        <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:content-id"></xop:Include>

    </
ns-prefix:attachment-node-name>
</
ns-prefix:operation-name>


    1.1. Operation Name
   
If  you want to talk to a Web service, you need to know two things in advance. One, the Web service endpoint URI. A Web service endpoint is a (referenceable) entity, processor, or resource to which Web service messages can be addressed [4]. This is basically the port you use to access a service hosted on a remote machine. So if you know the endpoint URI you can safely access it. Two, you need to specify the operation you are interested in. In our payload template, operation-name is where the operation name fits in. If we take our simple Print Order Manager service as an example, it will have an operation called placeOrder which places the print orders at the printing department.

You don't have to worry about the operation name when you are on the server side. Give a meaningful name for the operation-name to identify the response payload.

If you are quite new to XML, you might be wondering what  ns-prefix is. Name prefixes are used to resolve name conflicts in XML [5].

Note: This is not the only way to specify the operation name in WSF/PHP. When you are using WS-Addressing, you can specify the action which in turn gets mapped to the corresponding operation name on the server side. You can refer to the WSF/PHP manual to learn more about using WS-Addressing with WSF/PHP [6].

Our print client will be using the following payload to call the operation on the server side.

<payload:placeOrder xmlns:payload="http://php.wsf.wso2/mtom">
   
...

</
payload:placeOrder>


You will get the following payload as the response for a placeOrder call.

<payload:placeOrderResponse xmlns:payload="http://php.wsf.wso2/mtom">
   
...

</
payload:placeOrderResponse>


    1.2. Attachment Node

An attachment node carries all the information related to an attachment. Hence your payload should possess an attachment node for each and every attachment that it intends to carry. Let's see what information you need to have in this node.
   
First thing is the name of the node. There are no restrictions when naming a node. If you want to have the same name for every attachment node, you can still do so by putting them under a root node. For example, if you want to send multiple attachments, you can have a root node called Attachments and have Attachment nodes under that node.

An attachment node has an attribute called 'contentType' to specify the content type of the attachment it carries. This attribute name has to be prefixed by xmlmime and hence the namespace URI also has to be specified. In brief,  you should have the attribute name xmlmime:contentType in every attachment node specifying the content type of the binary content. The xmlmime namespace URI may be specified in the attachment node itself or in a root node, depending on your requirement.
There are different content types available such as image/jpeg, image/gif, application/binary, text/plain, audio/basic and video/mpeg. In case you do not specify the content type, the default will be application/octet-stream. The service will use this information to carry out content sensitive data processing. So specifying the correct content type is quite important from the service point of view.

The next important thing is the Include node found inside the attachment node. It is imperative that the include node name be prefixed with xop where the prefix name space URI is http://www.w3.org/2004/08/xop/include, and have an attribute named href with the value cid:content-id where content-id is a unique identifier for the attachment content. We will get back to the content ID when we talk about how to attach content into the payload.

This is all you need to know about the payload when it comes to sending binary attachments. Later we will see how the payload is seen when it is sent over the wire. Let's see how our payload looks like with the attachment nodes in place.

<payload:placeOrder xmlns:payload="http://php.wsf.wso2/mtom">
    
<payload:photos>
       
<payload:photo>
            <payload:content xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
           
    <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:photo-1"></xop:Include>
            </
payload:content>
       
</payload:photo>
        ...
       
<payload:photo>
            <payload:content xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
           
    <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:photo-n"></xop:Include>
            </
payload:content>
       
</payload:photo>
        </payload:photos>
</payload:placeOrder>

 

<payload:placeOrderResponse xmlns:payload='http://php.wsf.wso2/mtom'>Order placed !!!</payload:placeOrderResponse>



Step 2 : Creating a Client/Server

Even if you are an experienced Web developer, it is quite a bit of work to get a decent client/server application to run. However, we have done the tricky part already. Creating a client or a server using WSF/PHP is quite straight forward. Let's see how to create a client and a server, and also how your client/server can send binary attachments with the aid of an optimization mechanism called MTOM.
   
MTOM stands for Message Transmission Optimization Mechanism and it is a mechanism of exchanging binary data efficiently between Web applications [7]. MTOM uses XOP (XML-binary Optimized Packaging) [8] to pack binary data into the SOAP message. You have the option of specifying whether you want the out going attachments to be optimized for transmission or not when you create a client or a service. The default behavior is to send binary content with MTOM optimization.

This is how you create a client that is capable of sending attachments with MTOM support.

$client = new WSClient(array("useMTOM" => TRUE));


If  you set the value of  useMTOM to FALSE, the binary content will be sent as its Base64 encoded equivalent. The receiver should be ready to accept optimized data. This is how you create a service that is ready to accept MTOM optimized binary content sent by clients.

$service = new WSService(array("requestXOP" => TRUE,
                               ...));


It is needless to say that if you set requestXOP to FALSE, the service will not accept optimized binary data. Similarly, look at the following code samples to see how you can create a service that is capable of sending optimized binary data and a client that is ready to accept optimized binary data sent from a service.

$service = new WSService(array("useMTOM" => TRUE,
                               ...));

 

$client = new WSClient(array("responseXOP" => TRUE));


Now that you have the payload and the sending entity (i.e., client/service) ready,  can you send attachments? No, you cannot, because we still did not bind a binary data source to our payload. This data source can be a memory chunk or a physical file in some non-volatile data store. In the next step we will see how to attach a binary data source to the payload.


Step 3 : Binding Binary Data Content to the Payload


Before we move on, let's have a small prelude on the message payload formats used by clients and services. As mentioned in the WSF/PHP API documentation [9], the clients and services use two types of payload format. First is a raw XML stream similar to our blueprint payload mentioned in the first step. The second and the most versatile format is the WSMessage structure [10]. If you are going to use MTOM/XOP, it is a must that you use WSMessage as the payload format.

Getting back to our topic, there is a property called attachments in WSMessage. It is an array of binary content and you can specify all your binary content details in there. The binary content is stored against a content ID in this array. Here the content ID is the value you specify next to cid: in the attribute name href found under each attachment node in your payload XML. The following code snippet shows how you bind binary data content to the payload.

// Load binary data content
$photo-1Content = file_get_contents("./photos/fishing.jpg");
...
$photo-nContent = file_get_contents("./photos/sunset.jpg");

// Bind binary data content to the payload
$attachmentArray = array("photo-1" =>
$photo-1Content,
                         ...
                         "photo-n" => $photo-nContent);

$requestMessage = new WSMessage(...,
                                array("attachments" => 
$attachmentArray));


Now we are about to send the attachments. In the next step, we will see how a client sends the prepared message to a service and vice-versa.


Step 4 : Sending the Message with Attachments


A client basically has two ways of sending messages: request and send [9]. You can choose either of them depending on your requirement. A service may pack the attachments inside its operation implementation, and return the message so that the client will receive it [11].

In step 1, I said you can see how a message looks like when it is being sent over the wire. This is the ideal place to see that. For simplicity, let's take the following payload as our sample payload.

<payload:placeOrder xmlns:payload="http://php.wsf.wso2/mtom">
    
<payload:photos>
       
<payload:photo>
            <payload:content xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
           
    <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:photo-1"></xop:Include>
            </
payload:content>
       
</payload:photo>
        </payload:photos>
</payload:placeOrder>


This is what is looks like when optimized binary data is being sent. It is interesting to note what has happened to the content ID we set. Also note how the binary data is embedded within the MIME boundaries.

POST /photo_print_server/order_manager.php HTTP/1.1
User-Agent: Axis2/C
Content-Length: 126289
Content-Type: multipart/related; boundary=MIMEBoundary0c80a4a8-1f01-42a6-9161-1bbf5053fd6c; type="application/xop+xml"; start="<0.d1cdcb4c-0d1a-4354-a9e8-c506d3358dc2@apache.org>"; start-info="application/soap+xml"; charset="UTF-8"
Host: 127.0.0.1:3030

--MIMEBoundary0c80a4a8-1f01-42a6-9161-1bbf5053fd6c
content-transfer-encoding: binary
content-id: <0.d1cdcb4c-0d1a-4354-a9e8-c506d3358dc2@apache.org>
content-type: application/xop+xml; charset=UTF-8; type="application/soap+xml";
   <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
     <soapenv:Header/>
     <soapenv:Body>
       <payload:placeOrder xmlns:payload="http://php.wsf.wso2/mtom">                
         <payload:photos>
           <payload:photo>                
             <payload:content xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
               <xop:Include
xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:1.6e09048c-39a7-4fcb-9741-6116299a168b@apache.org"/>
             </payload:content>                 
           </payload:photo>    
         </payload:photos>        
       </payload:placeOrder>
     </soapenv:Body>
     </soapenv:Envelope>
--MIMEBoundary0c80a4a8-1f01-42a6-9161-1bbf5053fd6c
content-transfer-encoding:
binarycontent-id:<1.6e09048c-39a7-4fcb-9741-6116299a168b@apache.org>
content-type: image/jpeg

ÿØÿà<ðSÂß5ëOÚâýÏŽ¿g_Úãà§À…vßî~9ü:ð/Åø´j'ðÆž×¾Ò> xûÂϪh¾øÛñrûüñQðOļecâŸø}Ó÷£'9ÉÎs’I?žsôôãÒ¼£áOÀ�‚_lüK§üøGðÛá%—�< O«xׯß<áßÅâ¯k·×z–­âOO°:αyy}rí}¨‰âŠEµ·x­"†À=`õ9;¹& lt;úó×ñëþ4”Q@Q@Q@Q@Q@Q@Q@ÿÙ
...
--MIMEBoundary0c80a4a8-1f01-42a6-9161-1bbf5053fd6c--


This is what it looks like when unoptimized binary data is being sent.

POST /photo_print_server/order_manager.php HTTP/1.1
User-Agent: Axis2/C
Content-Length: 167445
Content-Type: application/soap+xml;charset=UTF-8
Host: 127.0.0.1:3030

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
  <soapenv:Header/>
  <soapenv:Body>
    <payload:placeOrder xmlns:payload="http://php.wsf.wso2/mtom>
      <payload:photos>
        <payload:photo>                
          <payload:content xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
            /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQECAgMCAgI
            ...
            l2Mp4ODzpUkKBPc3wYkYoUJymIhPmykMCnUPH/9k=
          </payload:content>
        </payload:photo>    
      </payload:photos>
    </payload:placeOrder>
  </soapenv:Body>
</soapenv:Envelope>



Step 5 : Processing Received Attachments


Any receiving entity irrespective of whether it is a client or a server, will want to process the received attachments. The recipient would process them depending on its requirement.  It would be great if the framework extracts the binary content from the SOAP message and puts it in an easily accessible data structure, so that the recipient programmer would not have to parse the XML stream, extract each element, validate, etc,. It is also very cumbersome to access binary data in a SOAP message as MIME bodies.
WSF/PHP can do all this for us - and we have already seen how it is done. When you create a client, you set the property responseXOP to TRUE, and when you create a service you set the property requestXOP to TRUE. That in turn populates two member variables in the WSMessage object.
The variable attachments will have binary content against the content ID and the variable cid2contentType will have the content type against the content ID. With this information, you can do anything with the received binary content.


Using the Sample Application


Our sample application will simulate a simple client/server application that supports sending image file attachments. Please note that this is merely an application to demonstrate how to send and receive binary attachments using WSF/PHP, and not meant to be used as a standard application.

    1. Download the client module and the service module.
    2. Extract photo_print_client.zip and deploy it inside the document root of your Apache Web server.
    3. Extract photo_print_server.zip and deploy it inside the document root of your Apache Web server (you can deploy the server module on another machine as well).
    4. Open include.php in your photo_print_client directory and set the value of  $service_uri to point to the directory where you have deployed your photo_print_server directory.
          E.g. The value of $service_uri should be http://localhost/ if you deploy your photo_print_server in htdocs of your Apache server that runs locally and listens on port 80.
    5. Start your Apache server and go to photo_print_client.php using your favorite Web browser. You would see something like the following on your Web browser.

         Screen Shot

        The rest of the functions are self explanatory. 

    6. If you want to try the application with your own photos, just put them in the photos directory in your photo_print_client. The photos have to be .jpg images.
    7. Use the Web interface or go to the orders directory in your photo_print_server directory to see the created orders.

 
Summary


In this article, I discussed how to send and receive binary attachments with WSF/PHP. Here is a recap of what we did. First you created the payload so that it possessed an attachment node for each and every binary attachment that it is going to carry. Then you created the sending entity (a client or a server) so that it is capable of sending optimized or unoptimized binary data. After that, you created an object of WSMessage encapsulating the payload and an array containing binary content. Finally, you sent the message. 

 

References

[1] http://www.w3.org/XML/
[2] http://www.w3.org/TR/soap/
[3] http://www.w3.org/Protocols/
[4] http://www.w3.org/TR/2005/WD-ws-addr-core-20050215/
[5] http://www.w3.org/TR/REC-xml-names/
[6] http://wso2.org/project/wsf/php/1.2.0/docs/manual.html
[7] http://www.w3.org/TR/soap12-mtom/
[8] http://www.w3.org/TR/xop10/
[9] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#method
[10] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#message
[11] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#method1

 

Author

Danushka Menikkumbura is a Senior Software Engineer at WSO2. danushka at wso2 dot com

AttachmentSize
photo_print_client.zip355.11 KB
photo_print_server.zip6.68 KB
0
No votes yet
Tags :