A DICOMweb WADO-RS retrieve (for example, GET .../studies/{studyUID}) returns a multipart/related body that can contain many DICOM instances. Buffering that whole body in memory does not scale. qie.parseMultipartContentFromFile() lets you stream the response straight to disk and then parse it part-by-part, writing each part to its own file, so the body never has to sit in JVM memory.
The function returns an XML message that lists every part and the on-disk path to its extracted file. Your script then iterates the parts and does whatever it needs with each one — store via DIMSE C-STORE, copy to a network share, forward to another channel, and so on. Although this article is framed around WADO-RS, the function works for any multipart/related body that has been streamed to a file.
When to use it (vs. qie.parseMultipartContent)
| qie.parseMultipartContent | qie.parseMultipartContentFromFile |
|---|
| Input | The multipart body as an in-memory String or byte[] | A file path (String or File) to the streamed body |
| Where parts go | Base64-encoded inline in the XML (<content>) | Written to individual files on disk (<path>) |
| Memory use | Entire body buffered in memory | Bounded — parts are streamed to disk as they are read |
| Best for | Small responses already held in memory | Large WADO-RS retrieves and other big multipart/related bodies |
Rule of thumb: if the response could be large (multiple or full-resolution DICOM instances), stream it to disk and use qie.parseMultipartContentFromFile().
Prerequisite: stream the response to disk
This function reads a file, so the upstream Web Service step must stream the response to disk rather than buffer it in memory. In practice that means:
- Request a multipart/related response (the DICOMweb Accept header), and
- Pass a streamToFile target to qie.callRESTWebService(...) so the body is written to that path.
If the response is buffered in memory instead, you will get the base64 of the body in /httpResponse/content rather than a file path — and passing that to this function throws (see Troubleshooting).
Walkthrough
The pattern is three steps: stream the body to a file, parse it, then iterate and clean up.
var content = null;
// URL template for the endpoint
var urlTemplate = qie.evaluateTemplate(qie.getWsOperationUrl("All DICOM Web APIs", "studies/{mc:study_uid}"));
var parameterMap = qie.newParameterMap();
parameterMap.put("http.header.Accept", 'multipart/related; type="application/dicom"');
// Step 1: stream a large multipart/related response body to disk so
// the body never sits in JVM memory.
var tmpName = "/private/tmp/wado-response-" + qie.getUUID();
var responseBodyFile = qie.evaluateTemplate(tmpName + ".bin");
// Call a REST web service using the provided parameters
var value = qie.callRESTWebService(
"All DICOM Web APIs", // connection
urlTemplate, // url
"GET", // operation
content, // content
null, // contentType
parameterMap, // parameters
60000, // timeout
true, // fullResponse
null, // connectTimeout
false, // returnBytes
null, // tls
responseBodyFile); // streamToFile
// Step 2: parse the streamed multipart body into per-part files. The XML
// returned carries the on-disk path to each part.
var partsXml = qie.parseMultipartContentFromFile(responseBodyFile);
// Step 3: iterate the parts, process each one, then clean up.
var partCount = partsXml.getCount('/mtomContent/part');
for (var i = 1; i <= partCount; i++) {
var partPath = partsXml.getNode('/mtomContent/part[' + i + ']/path');
var partType = partsXml.getNode('/mtomContent/part[' + i + ']/partType');
// ... process partPath (e.g. send via DIMSE C-STORE, copy to a share, etc.) ...
qie.deleteFile(partPath); // remove the extracted part once consumed
}
qie.deleteFile(tmpName); // remove the folder parts were extractd too
qie.deleteFile(responseBodyFile); // remove the streamed response body file
What the function returns
An XML message with one <part> per multipart segment. Each part's name attribute is its Content-ID, <partType> is its Content-Type, and <path> is the absolute path of the extracted file on disk. (Part files are given an extension that matches their MIME type, e.g. .dcm for application/dicom.)
<mtomContent>
<part name="1.2.840.113619.2.1.1">
<partType>application/dicom</partType>
<path>/private/tmp/wado-response-3f2a.../part-1.dcm</path>
</part>
<part name="1.2.840.113619.2.1.2">
<partType>application/dicom</partType>
<path>/private/tmp/wado-response-3f2a.../part-2.dcm</path>
</part>
</mtomContent>
Read it with the usual XML message functions:
var partCount = partsXml.getCount('/mtomContent/part'); // how many parts
var firstPath = partsXml.getNode('/mtomContent/part[1]/path'); // first part's file
var firstType = partsXml.getNode('/mtomContent/part[1]/partType'); // first part's MIME type
Controlling where parts are written and how they are named
By default the parts are written to a sibling directory next to the input file (named after the input file with its last extension stripped — e.g. wado-response-{UUID}.bin → wado-response-{UUID}/), and each file is named part-1.{ext}, part-2.{ext}, … You can override both with the optional outputDir and filenamePrefix arguments:
var partsXml = qie.parseMultipartContentFromFile(
responseBodyFile, // inputFile
"/private/tmp/wado-extracted", // outputDir - directory to receive the part files
"study-1-instance" // filenamePrefix - files become study-1-instance-1.dcm, -2.dcm, ...
);
Clean up after yourself
The function does not delete anything. The streamed input file is left on disk after parsing, and every extracted part file remains until you remove it. Your script owns the lifecycle of both — call qie.deleteFile(...) on each part once you have consumed it and on the response body file when you are done (as shown in the walkthrough above). Skipping cleanup will leave files accumulating under the stream path.
Troubleshooting
| Symptom | Cause & fix |
|---|
| Error: "inputFile is too long to be a file path… looks like the response was buffered in memory rather than streamed" | You passed the response body (base64) instead of a file path. The Web Service mapping function buffered the body in memory because no Stream: path was configured. Set the Stream: field on the Web Service mapping function so the response body is streamed to disk, then pass the resulting responseBodyFile (not /httpResponse/content) to the function. |
| Error: "inputFile does not point to an existing local file…" | The streamed file was never written, usually for the same reason — the response was not actually streamed. Confirm the Stream: field is set on the Web Service mapping function (so callRESTWebService streams the body to responseBodyFile), the configured path resolves to a writable local directory, and the file wasn't removed by an earlier qie.deleteFile cleanup. |
| Error about NetFile / CIFS paths | The input (and output) must be a local file path or java.io.File. NetFile / CIFS paths are not supported — copy the body to a local path first. |
See Also