Writing Binary Files from a Script¶
When the goal is to land a binary file (PDF, image, ZIP, etc.) on disk (for archival, downstream pickup by another channel, or hand-off to a user) a mapping script can write it directly with qie.writeFile(path, bytes, true) at any point during channel processing, regardless of the channel's message format. The inbound message might be HL7, JSON, XML, or CSV; the binary content typically arrives as a Base64 field inside that message, in messageCache, or as the body of a REST response made from the script itself. Writing the file inline leaves the working message untouched, so the channel continues with its main job (ACKing the sender, writing to the database, logging, etc.) and the file-write happens as a side effect.
The trap most users hit is trying instead to push the binary bytes back into the working message. Assigning to / on a parsed HL7, JSON, CSV, or XML message re-parses the content as that format and throws, typically org.apache.commons.csv.CSVException: Invalid character between encapsulated token and delimiter or a similar MessageModel error. Writing the file inline with qie.writeFile sidesteps the working message entirely and avoids that problem.
The snippets below differ only in how the bytes are obtained.
If the binary payload arrives Base64-encoded inside HL7 OBX-5 with OBX-2 = ED, see Handling HL7 Base64 Attachments. It applies the same qie.writeFile pattern and also covers replacing OBX-5 with a reference and re-embedding from disk at the destination.
From a Base64 string in messageCache¶
Cache values are strings, so binary payloads stashed there by an upstream node are almost always Base64-encoded. Decode the string with qie.base64DecodeToBytes and hand the result to qie.writeFile. The true third argument creates the parent directory if it does not yet exist.
var base64Data = messageCache.getValue('pdfPayload');
if (StringUtils.isNotBlank(base64Data)) {
var bytes = qie.base64DecodeToBytes(base64Data);
var path = 'C:/qie-out/' + source.getNode('MSH-10') + '.pdf';
qie.writeFile(path, bytes, true);
}
Is it Base64 or raw bytes?
A real PDF starts with the literal bytes %PDF-. The same PDF Base64-encoded starts with JVBERi. If your cached value already begins with %PDF-, skip the decode step and pass the value to qie.writeFile as-is. If it begins with JVBERi, decode first.
From a REST or HTTP response¶
When the source of the bytes is a web service, call qie.callRESTWebService(...) with returnBytes = true so the response body comes back as a byte[] instead of being decoded as text. Without that flag, the bytes get coerced through the current message format on assignment and you see the same CSVException-style error described above.
var url = qie.evaluateTemplate(qie.getWsEndpointUrl('DocumentService'));
var params = qie.newParameterMap();
var bytes = qie.callRESTWebService(
'DocumentService',
url,
'GET',
'',
'application/json',
params,
60000,
false, // fullResponse
60000, // connectTimeout
true // returnBytes
);
var path = 'C:/qie-out/' + source.getNode('MSH-10') + '.pdf';
qie.writeFile(path, bytes, true);
Naming the output file¶
Stable, message-derived names make files easy to match back to the originating message and prevent collisions when two messages arrive in the same second. Common choices:
- Message control id:
source.getNode('MSH-10')for HL7, or whatever node carries the source system's identifier. - Timestamp:
qie.formatDate('yyyyMMddHHmmss')for second-level resolution,yyyyMMddHHmmssSSSfor millisecond. - UUID:
qie.uuid()when no message field is suitable.
A typical combination, assuming bytes was obtained from one of the patterns above:
var fileName = source.getNode('MSH-10') + '-' + qie.formatDate('yyyyMMddHHmmssSSS') + '.pdf';
var path = 'C:/qie-out/' + fileName;
qie.writeFile(path, bytes, true);
What to do with the channel's outbound message¶
The script above writes the file as a side effect; the message object is untouched. Two reasonable channel shapes:
- Continue processing. Route the original message to a real destination: a database write, an ACK back to the sender, log to another system. The file-write is incidental to the channel's main job.
- Stop here. If writing the file is the channel's job, end with a Discard Sender so the channel completes cleanly without any further side effects.
Alternative: route the bytes through a File Sender¶
If you want File Sender's built-in retry, filename-pattern handling, or after-processing options (move-to-archive, delete, etc.), rebind message to a binary message and let the destination write the file. Again, bytes is whatever byte[] you obtained from one of the patterns above:
Then configure the destination as a File Sender with a path like C:/qie-out/*.pdf. The rebind step is what avoids the CSVException. qie.createBinaryMessage() replaces the working message with a fresh binary model that holds bytes verbatim.
Why rebinding is needed
Once message is bound to an HL7, CSV, JSON, or XML model, every assignment is parsed through that model. Binary bytes are not valid in any of those formats, so the parse fails before the destination ever runs. Creating a new binary message swaps the model out before you assign the bytes.
See the Binary Functions, File Functions, New Message Functions, and Web Service Functions sections of the Code Wizard reference for full signatures and the complete list of related functions.