fix: Microscopy bulkdata and image retrieve (#3894)
This commit is contained in:
parent
7014e6ca4a
commit
7fac49b449
@ -1,4 +1,25 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import fixMultipart from './fixMultipart';
|
||||
|
||||
const { DICOMwebClient } = api;
|
||||
|
||||
const anyDicomwebClient = DICOMwebClient as any;
|
||||
|
||||
// Ugly over-ride, but the internals aren't otherwise accessible.
|
||||
if (!anyDicomwebClient._orig_buildMultipartAcceptHeaderFieldValue) {
|
||||
anyDicomwebClient._orig_buildMultipartAcceptHeaderFieldValue =
|
||||
anyDicomwebClient._buildMultipartAcceptHeaderFieldValue;
|
||||
anyDicomwebClient._buildMultipartAcceptHeaderFieldValue = function (mediaTypes, acceptableTypes) {
|
||||
if (mediaTypes.length === 1 && mediaTypes[0].mediaType.endsWith('/*')) {
|
||||
return '*/*';
|
||||
} else {
|
||||
return anyDicomwebClient._orig_buildMultipartAcceptHeaderFieldValue(
|
||||
mediaTypes,
|
||||
acceptableTypes
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of the static wado client, that fetches data from
|
||||
@ -25,9 +46,50 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
modality: '00080060',
|
||||
};
|
||||
|
||||
constructor(qidoConfig) {
|
||||
super(qidoConfig);
|
||||
this.staticWado = qidoConfig.staticWado;
|
||||
protected config;
|
||||
protected staticWado;
|
||||
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.staticWado = config.staticWado;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle improperly specified multipart/related return type.
|
||||
* Note if the response is SUPPOSED to be multipart encoded already, then this
|
||||
* will double-decode it.
|
||||
*
|
||||
* @param options
|
||||
* @returns De-multiparted response data.
|
||||
*
|
||||
*/
|
||||
public retrieveBulkData(options): Promise<any[]> {
|
||||
const shouldFixMultipart = this.config.fixBulkdataMultipart !== false;
|
||||
const useOptions = {
|
||||
...options,
|
||||
};
|
||||
if (this.staticWado) {
|
||||
useOptions.mediaTypes = [{ mediaType: 'application/*' }];
|
||||
}
|
||||
return super
|
||||
.retrieveBulkData(useOptions)
|
||||
.then(result => (shouldFixMultipart ? fixMultipart(result) : result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves instance frames using the image/* media type when configured
|
||||
* to do so (static wado back end).
|
||||
*/
|
||||
public retrieveInstanceFrames(options) {
|
||||
if (this.staticWado) {
|
||||
return super.retrieveInstanceFrames({
|
||||
...options,
|
||||
mediaTypes: [{ mediaType: 'image/*' }],
|
||||
});
|
||||
} else {
|
||||
return super.retrieveInstanceFrames(options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
function checkToken(token, data, dataOffset): boolean {
|
||||
if (dataOffset + token.length > data.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let endIndex = dataOffset;
|
||||
|
||||
for (let i = 0; i < token.length; i++) {
|
||||
if (token[i] !== data[endIndex++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function stringToUint8Array(str: string): Uint8Array {
|
||||
const uint = new Uint8Array(str.length);
|
||||
|
||||
for (let i = 0, j = str.length; i < j; i++) {
|
||||
uint[i] = str.charCodeAt(i);
|
||||
}
|
||||
|
||||
return uint;
|
||||
}
|
||||
|
||||
function findIndexOfString(
|
||||
data: Uint8Array,
|
||||
str: string,
|
||||
offset?: number
|
||||
): number {
|
||||
offset = offset || 0;
|
||||
|
||||
const token = stringToUint8Array(str);
|
||||
|
||||
for (let i = offset; i < data.length; i++) {
|
||||
if (token[0] === data[i]) {
|
||||
// console.log('match @', i);
|
||||
if (checkToken(token, data, i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
export default findIndexOfString;
|
||||
@ -0,0 +1,70 @@
|
||||
import findIndexOfString from './findIndexOfString';
|
||||
|
||||
/**
|
||||
* Fix multipart data coming back from the retrieve bulkdata request, but
|
||||
* incorrectly tagged as application/octet-stream. Some servers don't handle
|
||||
* the response type correctly, and this method is relatively robust about
|
||||
* detecting multipart data correctly. It will only extract one value.
|
||||
*/
|
||||
export default function fixMultipart(arrayData) {
|
||||
const data = new Uint8Array(arrayData[0]);
|
||||
// Don't know the exact minimum length, but it is at least 25 to encode multipart
|
||||
if (data.length < 25) {
|
||||
return arrayData;
|
||||
}
|
||||
const dashIndex = findIndexOfString(data, '--');
|
||||
if (dashIndex > 6) {
|
||||
return arrayData;
|
||||
}
|
||||
const tokenIndex = findIndexOfString(data, '\r\n\r\n', dashIndex);
|
||||
if (tokenIndex > 512) {
|
||||
// Allow for 512 characters in the header - there is no apriori limit, but
|
||||
// this seems ok for now as we only expect it to have content type in it.
|
||||
return arrayData;
|
||||
}
|
||||
const header = uint8ArrayToString(data, 0, tokenIndex);
|
||||
// Now find the boundary marker
|
||||
const responseHeaders = header.split('\r\n');
|
||||
const boundary = findBoundary(responseHeaders);
|
||||
|
||||
if (!boundary) {
|
||||
return arrayData;
|
||||
}
|
||||
// Start of actual data is 4 characters after the token
|
||||
const offset = tokenIndex + 4;
|
||||
|
||||
const endIndex = findIndexOfString(data, boundary, offset);
|
||||
if (endIndex === -1) {
|
||||
return arrayData;
|
||||
}
|
||||
|
||||
return [data.slice(offset, endIndex - 2).buffer];
|
||||
}
|
||||
|
||||
export function findBoundary(header: string[]): string {
|
||||
for (let i = 0; i < header.length; i++) {
|
||||
if (header[i].substr(0, 2) === '--') {
|
||||
return header[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function findContentType(header: string[]): string {
|
||||
for (let i = 0; i < header.length; i++) {
|
||||
if (header[i].substr(0, 13) === 'Content-Type:') {
|
||||
return header[i].substr(13).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function uint8ArrayToString(data, offset, length) {
|
||||
offset = offset || 0;
|
||||
length = length || data.length - offset;
|
||||
let str = '';
|
||||
|
||||
for (let i = offset; i < offset + length; i++) {
|
||||
str += String.fromCharCode(data[i]);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
@ -16,6 +16,7 @@ import { ContextMenuController, CustomizableContextMenuTypes } from './Customiza
|
||||
import * as dicomWebUtils from './DicomWebDataSource/utils';
|
||||
import { createReportDialogPrompt } from './Panels';
|
||||
import createReportAsync from './Actions/createReportAsync';
|
||||
import StaticWadoClient from './DicomWebDataSource/utils/StaticWadoClient';
|
||||
|
||||
const defaultExtension: Types.Extensions.Extension = {
|
||||
/**
|
||||
@ -54,4 +55,5 @@ export {
|
||||
dicomWebUtils,
|
||||
createReportDialogPrompt,
|
||||
createReportAsync,
|
||||
StaticWadoClient,
|
||||
};
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import { errorHandler, DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
const { DICOMwebClient } = api;
|
||||
|
||||
DICOMwebClient._buildMultipartAcceptHeaderFieldValue = () => {
|
||||
return '*/*';
|
||||
};
|
||||
import { StaticWadoClient } from '@ohif/extension-default';
|
||||
|
||||
/**
|
||||
* create a DICOMwebClient object to be used by Dicom Microscopy Viewer
|
||||
@ -31,7 +25,7 @@ export default function getDicomWebClient({ extensionManager, servicesManager })
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
const client = new api.DICOMwebClient(wadoConfig);
|
||||
const client = new StaticWadoClient(wadoConfig);
|
||||
client.wadoURL = wadoConfig.url;
|
||||
|
||||
if (extensionManager.activeDataSource === 'dicomlocal') {
|
||||
|
||||
@ -103,7 +103,7 @@ window.config = {
|
||||
supportsFuzzyMatching: false,
|
||||
supportsWildcard: true,
|
||||
staticWado: true,
|
||||
singlepart: 'bulkdata,video,pdf',
|
||||
singlepart: 'video,pdf',
|
||||
bulkDataURI: {
|
||||
enabled: true,
|
||||
relativeResolution: 'studies',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user