SAP Integration Suite – Attachment Handling

Estimated read time 10 min read

Dear Community,

this blog provides practical insights into handling attachments within the Cloud Integration capability of the SAP Integration Suite

Attachment handling is well documented in the official SAP Help Portal:  SAP HELP – Attachment Handling

Requirement: Process a SOAP message that contains multipart/related attachments. A common approach is to include these attachments in the message body encoded in Base64 format.* 

*Base64 Format is a method for encoding binary data into a string of ASCII characters, it is often used to transmit data that must be handled in text form. 

Approach: Let’s cover all of this in a Groovy script and ideally make it reusable via ProcessDirect. 

 

 

 

 

 

 

Parameter as Table: 

Source TypeSource ValueNamechunksizeconstant850singledatachunkbase64constanttrue/falsedatachunkbase64constanttrue/falsesinglebase64constanttrue/falsenode_nameconstantattachmentsreplace_payloadconstanttrue/false

Solution:

Line 10–15: Some configuration details will be loadedLine 17: All attachments are loaded into a key-value mapLine 19–34: Check if attachments exist, prepare a new XML node, and prepare the chunk size if requiredLine 34 – 78: Iterate over all attachments in the given message and process them according to the external parameter configuration, a new xml with attachments is createdLine 81 – 100: Replace the current payload body with the attachments encoded in Base64, or add the new attachments XML at a specific node in the original payloadimport com.sap.gateway.ip.core.customdev.util.Message
import java.util.Map
import java.util.Iterator
import javax.activation.DataHandler

def Message processData(Message message) {

def replacepayload = message.getProperty(“replace_payload”).toLowerCase() as String;
def nodename = message.getProperty(“node_name”) as String;
def singlebase64 = message.getProperty(“singlebase64”).toLowerCase() as String;
def datachunkbase64 = message.getProperty(“datachunkbase64”).toLowerCase() as String;
def singledatachunkbase64 = message.getProperty(“singledatachunkbase64”).toLowerCase() as String;
def chunksize = message.getProperty(“chunksize”) as Integer;

Map < String, DataHandler > attachments = message.getAttachments();

if (!attachments.isEmpty())
{

def body = message.getBody(java.io.Reader);
def xmlSlurper = new XmlSlurper()
def iattachments = new Node(null, ‘root’);

//split base64 string in small chunks
def chunkSize = chunksize
int c = 0;
Iterator < DataHandler > it = attachments.values().iterator();
int i = 1;

// Loop attachments in SOAP Adapter and convert to Base64 String

while (it.hasNext())
{
// bas64 conversion takes place here
DataHandler attachment = it.next();
InputStream inputStream = attachment.getInputStream()
byte[] binaryData = inputStream.bytes;
String base64 = Base64.getEncoder().encodeToString(binaryData);
// bas64 conversion takes place here

def item = new Node(iattachments,’item’,[SEGMENT: i])
new Node(item,’FILE_ID’,i);
new Node(item,’FILE’,attachment.getName());
new Node(item,’FILESIZE’,binaryData.length);
new Node(item,’DESCRIPTION’,attachment.getName());

if (singlebase64 == ‘true’) {
// example for single base64 line in XML structure
new Node(item,’base64′,base64);
// example for single base64 line in XML structure
}

if (datachunkbase64 == ‘true’) {
// example for base64 chunks in XML structure covert under multiple data nodes
def base64Aschunksv1 = new Node(item,’base64′,[SEGMENT: i]);
for (int y = 0; y < base64.length(); y += chunkSize) {
int endIndex = Math.min(y + chunkSize, base64.length())
new Node(base64Aschunksv1,’data’,base64.substring(y, endIndex).toUpperCase());
}
// example for base64 chunks in XML structure covert under multiple data nodes
}

if (singledatachunkbase64 == ‘true’) {
// example for multiple base64 chunks in XML structure each chunk contains a data node
for (int y = 0; y < base64.length(); y += chunkSize) {
int endIndex = Math.min(y + chunkSize, base64.length())
c++;
def base64Aschunksv2 = new Node(item,’base64′,[SEGMENT: c]);
new Node(base64Aschunksv2,’data’,base64.substring(y, endIndex).toUpperCase());
}
// example for multiple base64 chunks in XML structure each chunk contains a data node
}
message.setProperty(“hasAttachment” + i, “true”);
i++;
c = 0;
}

def iattachmentxml = groovy.xml.XmlUtil.serialize(iattachments);

if (replacepayload == ‘true’) {
// ####################### Option 1: Attachments as payload – ignore incomming payload #######################
message.setBody(iattachmentxml);
// ####################### Option 1: Attachments as payload – ignore incomming payload #######################
} else {
// ####################### Option 2: Merge existing XML with new created attachment XML a a specific node #######################
def Node1 = xmlSlurper.parseText(iattachmentxml)
def Node2 = xmlSlurper.parse(body)

// // find a specific node to insert the attachments
def nodetoadd = Node2.’**’.find{it.name() == nodename }
nodetoadd.appendNode(Node1.’item’)
def mergedXml = groovy.xml.XmlUtil.serialize(Node2)

message.setBody(mergedXml);
// ####################### Option 2: Merge existing XML with new created attachment XML a a specific node #######################
}
}

return message
}

Results:
Parameters to be configured:

 

ParameterOptionResult/illustration1_replace_payloadSetting this parameter to True will replace the original payload with a <root><item> block that contains the attachments.

1_replace_payload

If set to false in combination with 1_node_name, the payload will not be replaced, and the attachments will be added under the specified node instead.1_node_nameNode name in the payload under which the attachments node should be added (e.g., attachments).2_chunk_sizeThe size of the data chunk in bytes (default is 850). 2_data_chunk_base64Set to true or false to enable or disable Base64 encoding with multiple <data> chunk nodes.2_single_base64Set to true or false to enable or disable the use of a single <base64> node.2_single_data_chunk_base64Set to true or false to enable or disable the use of multiple <base64> nodes, each containing multiple <data> nodes.

Conclusion:

This attachment handling approach allows you to process your messages as a single payload, including attachments encoded in Base64 format. Whenever an Attachments section is available in your message (e.g., when using the Mail Adapter), this method—using DataHandler and related techniques—enables you to process that content effectively.

Of course, there may be slightly different requirements depending on your specific use case. However, this should provide a solid starting point for individual adjustments and customization.

 

 

​ Dear Community,this blog provides practical insights into handling attachments within the Cloud Integration capability of the SAP Integration SuiteAttachment handling is well documented in the official SAP Help Portal:  SAP HELP – Attachment HandlingRequirement: Process a SOAP message that contains multipart/related attachments. A common approach is to include these attachments in the message body encoded in Base64 format.* *Base64 Format is a method for encoding binary data into a string of ASCII characters, it is often used to transmit data that must be handled in text form. Approach: Let’s cover all of this in a Groovy script and ideally make it reusable via ProcessDirect.       Parameter as Table: Source TypeSource ValueNamechunksizeconstant850singledatachunkbase64constanttrue/falsedatachunkbase64constanttrue/falsesinglebase64constanttrue/falsenode_nameconstantattachmentsreplace_payloadconstanttrue/falseSolution:Line 10–15: Some configuration details will be loadedLine 17: All attachments are loaded into a key-value mapLine 19–34: Check if attachments exist, prepare a new XML node, and prepare the chunk size if requiredLine 34 – 78: Iterate over all attachments in the given message and process them according to the external parameter configuration, a new xml with attachments is createdLine 81 – 100: Replace the current payload body with the attachments encoded in Base64, or add the new attachments XML at a specific node in the original payloadimport com.sap.gateway.ip.core.customdev.util.Message
import java.util.Map
import java.util.Iterator
import javax.activation.DataHandler

def Message processData(Message message) {

def replacepayload = message.getProperty(“replace_payload”).toLowerCase() as String;
def nodename = message.getProperty(“node_name”) as String;
def singlebase64 = message.getProperty(“singlebase64”).toLowerCase() as String;
def datachunkbase64 = message.getProperty(“datachunkbase64”).toLowerCase() as String;
def singledatachunkbase64 = message.getProperty(“singledatachunkbase64”).toLowerCase() as String;
def chunksize = message.getProperty(“chunksize”) as Integer;

Map < String, DataHandler > attachments = message.getAttachments();

if (!attachments.isEmpty())
{

def body = message.getBody(java.io.Reader);
def xmlSlurper = new XmlSlurper()
def iattachments = new Node(null, ‘root’);

//split base64 string in small chunks
def chunkSize = chunksize
int c = 0;
Iterator < DataHandler > it = attachments.values().iterator();
int i = 1;

// Loop attachments in SOAP Adapter and convert to Base64 String

while (it.hasNext())
{
// bas64 conversion takes place here
DataHandler attachment = it.next();
InputStream inputStream = attachment.getInputStream()
byte[] binaryData = inputStream.bytes;
String base64 = Base64.getEncoder().encodeToString(binaryData);
// bas64 conversion takes place here

def item = new Node(iattachments,’item’,[SEGMENT: i])
new Node(item,’FILE_ID’,i);
new Node(item,’FILE’,attachment.getName());
new Node(item,’FILESIZE’,binaryData.length);
new Node(item,’DESCRIPTION’,attachment.getName());

if (singlebase64 == ‘true’) {
// example for single base64 line in XML structure
new Node(item,’base64′,base64);
// example for single base64 line in XML structure
}

if (datachunkbase64 == ‘true’) {
// example for base64 chunks in XML structure covert under multiple data nodes
def base64Aschunksv1 = new Node(item,’base64′,[SEGMENT: i]);
for (int y = 0; y < base64.length(); y += chunkSize) {
int endIndex = Math.min(y + chunkSize, base64.length())
new Node(base64Aschunksv1,’data’,base64.substring(y, endIndex).toUpperCase());
}
// example for base64 chunks in XML structure covert under multiple data nodes
}

if (singledatachunkbase64 == ‘true’) {
// example for multiple base64 chunks in XML structure each chunk contains a data node
for (int y = 0; y < base64.length(); y += chunkSize) {
int endIndex = Math.min(y + chunkSize, base64.length())
c++;
def base64Aschunksv2 = new Node(item,’base64′,[SEGMENT: c]);
new Node(base64Aschunksv2,’data’,base64.substring(y, endIndex).toUpperCase());
}
// example for multiple base64 chunks in XML structure each chunk contains a data node
}
message.setProperty(“hasAttachment” + i, “true”);
i++;
c = 0;
}

def iattachmentxml = groovy.xml.XmlUtil.serialize(iattachments);

if (replacepayload == ‘true’) {
// ####################### Option 1: Attachments as payload – ignore incomming payload #######################
message.setBody(iattachmentxml);
// ####################### Option 1: Attachments as payload – ignore incomming payload #######################
} else {
// ####################### Option 2: Merge existing XML with new created attachment XML a a specific node #######################
def Node1 = xmlSlurper.parseText(iattachmentxml)
def Node2 = xmlSlurper.parse(body)

// // find a specific node to insert the attachments
def nodetoadd = Node2.’**’.find{it.name() == nodename }
nodetoadd.appendNode(Node1.’item’)
def mergedXml = groovy.xml.XmlUtil.serialize(Node2)

message.setBody(mergedXml);
// ####################### Option 2: Merge existing XML with new created attachment XML a a specific node #######################
}
}

return message
}Results:Parameters to be configured: ParameterOptionResult/illustration1_replace_payloadSetting this parameter to True will replace the original payload with a <root><item> block that contains the attachments.1_replace_payloadIf set to false in combination with 1_node_name, the payload will not be replaced, and the attachments will be added under the specified node instead.1_node_nameNode name in the payload under which the attachments node should be added (e.g., attachments).2_chunk_sizeThe size of the data chunk in bytes (default is 850). 2_data_chunk_base64Set to true or false to enable or disable Base64 encoding with multiple <data> chunk nodes.2_single_base64Set to true or false to enable or disable the use of a single <base64> node.2_single_data_chunk_base64Set to true or false to enable or disable the use of multiple <base64> nodes, each containing multiple <data> nodes.Conclusion:This attachment handling approach allows you to process your messages as a single payload, including attachments encoded in Base64 format. Whenever an Attachments section is available in your message (e.g., when using the Mail Adapter), this method—using DataHandler and related techniques—enables you to process that content effectively.Of course, there may be slightly different requirements depending on your specific use case. However, this should provide a solid starting point for individual adjustments and customization.    Read More Technology Blog Posts by SAP articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author