import base64
# flow.getVariable("request.content") and decodedContent type are class org.python.core.PyUnicode
decodedContent = base64.b64decode(flow.getVariable("request.content"))
flow.setVariable('request.header.Content-Type','multipart/form-data; boundary=boundary123')
attachmentBody = ""
attachmentBody += "--boundary123\r\n"
attachmentBody += 'Content-Disposition: form-data; name="file"; filename="testpy.png"\r\n'
attachmentBody += "Content-Type: image/png\r\n\r\n"
attachmentBody += decodedContent + "\r\n"
attachmentBody += "--boundary123--\r\n"
# attachmentBody type is class org.python.core.PyUnicode
# Modifying the request content
flow.setVariable('request.content', attachmentBody)
Solved! Go to Solution.
@dchiesa1 , I got it working with Jython without the need for jar files!! Thank you so much for the leads and your note and code sample about Java's "[B@1cf582a1", which is a string representation of byte a array.
The trickiest part was the data manipulation needed to be done in Java instead of Python. To accomplish this, we needed to chain the Java commands to prevent corruption. The code has been successfully tested with the following attachments: DOCX, XLSX, PDF, JPG, and PNG.
Below is the bare minimum solution along with comments.
# The flow.setVariable('debug-') is used to print messages to Apigee variables. This is especially needed when using the Apigee Emulator because Python's print function doesn't output to stepExecution-stdout.
# Why is Jython used for this script? RhinoJS doesn't provide a base64 function out of the box.
# Jython reduces Java's boilerplate, but it uses Python v2.
# When manipulating data in Jython, make sure to leverage Java's functions (use Java syntax) to prevent corruption.
import hashlib # This is only needed for debugging purposes. We use this to calculate the SHA-256 checksum for each step where the content is stored into a variable.
from java.util import Base64 # DO NOT USE Jython's base64. Jython's base64 returns PyUnicode (string). We want it to return PyArray (byte array).
from java.lang import String # Used to convert PyUnicode to Java String
from java.nio.charset import Charset # Used to convert Java String to Java byte array
from java.io import ByteArrayInputStream # Used to convert from Java byte array to Java bytes
# Debug>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
flow.setVariable('debug-OriginContentType', type(flow.getVariable("request.content"))) #org.python.core.PyUnicode
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<Debug
requestContent = flow.getVariable("request.content")
fileName = flow.getVariable("request.header.filename")
# Debug>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Checksum of base64-encoded data
sha256_base64Payload = hashlib.sha256(requestContent).hexdigest()
flow.setVariable('debug-sha256_base64Payload', sha256_base64Payload)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<Debug
# We use Java's Base64 so it returns PyArray. Jython's base64 returns PyUnicode
bytesPayload = Base64.getDecoder().decode(requestContent)
# Debug>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
flow.setVariable('debug-bytesPayloadType', type(bytesPayload)) # class org.python.core.PyArray
# with this SHA256, the result should match as we perform shasum -a 256 attachmentFileName
sha256_bytesPayload = hashlib.sha256(bytesPayload).hexdigest()
flow.setVariable('debug-sha256_bytesPayload', sha256_bytesPayload)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<Debug
# Constructing MIME-multipart
boundary = "----boundary123"
# MIME Header
MIMEHeaderStr = ""
MIMEHeaderStr += "--" + boundary + "\r\n"
MIMEHeaderStr += 'Content-Disposition: form-data; name="file"; filename="' + fileName + '"\r\n'
MIMEHeaderStr += "Content-Type: application/octet-stream\r\n\r\n"
# The MIMEStr is a PyUnicode (string). We need to convert it to byte array later before we join it with the image payload
# MIME Tail
MIMETailStr = "\r\n"
MIMETailStr += "--" + boundary + "--\r\n"
# Constructing MIME multipart by converting MIMEStr and MIMETtail from Pyunicode to PyArray.
# We need to chain the Java commands; otherwise, it will pickup Jython's and cause corruption
MIMEPayload = String(MIMEHeaderStr).getBytes(Charset.forName("UTF-8")) + bytesPayload + String(MIMETailStr).getBytes(Charset.forName("UTF-8"))
# Debug>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
flow.setVariable('debugMIMEHeaderLength', len(MIMEHeaderStr))
flow.setVariable('debugMIMEFooterLength', len(MIMETailStr))
flow.setVariable('debugbytesPayloadLength', len(bytesPayload))
flow.setVariable('debugMIMEPayloadLength', len(MIMEPayload))
flow.setVariable('debugaMIMEPayloadType', type(MIMEPayload)) # class org.python.core.PyArray
flow.setVariable('debuggingsha256_MIMEPayload', hashlib.sha256(MIMEPayload).hexdigest())
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<Debug
# Setting up final requests
flow.setVariable('request.header.content-type', 'multipart/form-data; boundary='+ boundary)
# Typically, the Target server will calculate the content length and overwrite what we send
# We calculate the length anyway, just in case the Target server doesn't do what it supposed to do
flow.setVariable('request.header.content-length', str(len(MIMEPayload)))
# DO NOT set request.content using flow.setVariable. It will return HTTP 400 because the request content something like `[B@beab919`.
# The `[B@beab919` is a string representation of a byte array
# Instead, use Java
message.setContent(ByteArrayInputStream (MIMEPayload))