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 [0]
Who should read this article? [0]
Step 1 : Creating a Payload [0]
1.1. Operation Name [0]
1.2. Attachment Node [0]
Step 2 : Creating a Client/Server [0]
Step 3 : Binding Binary Data Content to the Payload [0]
Step 4 : Sending a Message with Attachments [0]
Step 5 : Processing Received Attachments [0]
Using Sample Application [0]
Summary [0]
1 [0]]. 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 [0]]. HTTP is the transport that carries SOAP messages between peers [3 [0]]. 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> |
4 [0]]. 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 [0]].
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 [0]].
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> |
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> |
| $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.
9 [0]], 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 [0]]. 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.
9 [0]]. 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 [0]].
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> |
http://www.w3.org/XML/ [1]
[2] http://www.w3.org/TR/soap/ [2]
[3] http://www.w3.org/Protocols/ [3]
[4] http://www.w3.org/TR/2005/WD-ws-addr-core-20050215/ [4]
[5] http://www.w3.org/TR/REC-xml-names/ [5]
[6] http://wso2.org/project/wsf/php/1.2.0/docs/manual.html [5]
[7] http://www.w3.org/TR/soap12-mtom/ [6]
[8] http://www.w3.org/TR/xop10/ [7]
[9] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#method [7]
[10] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#message [7]
[11] http://wso2.org/project/wsf/php/1.2.0/docs/api_content.html#method1 [7]
Author
Danushka Menikkumbura is a Senior Software Engineer at WSO2. danushka at wso2 dot com