ohif-viewer/Packages/ohif-study-list/client/lib/exportStudies.js
2018-12-14 14:38:36 +01:00

203 lines
5.9 KiB
JavaScript

import { JSZip } from 'meteor/silentcicero:jszip';
import { OHIF } from 'ohif-core';
const getNumberOfFilesToExport = function(studiesToExport) {
let numberOfFilesToExport = 0;
studiesToExport.forEach(study => {
numberOfFilesToExport += getNumberOfFilesInStudy(study);
});
return numberOfFilesToExport;
}
const convertSizeToString = size => {
const measuments = ['B', 'KB', 'MB', 'GB'];
let totalBytes = size,
measumentIndex = 0;
while(totalBytes > 1024) {
totalBytes /= 1024;
measumentIndex++;
}
return `${totalBytes.toFixed(2)} ${measuments[measumentIndex]}`;
}
/**
* Exports requested studies
* @param studiesToExport Studies to export
*/
OHIF.studylist.exportStudies = studiesToExport => {
if (studiesToExport.length < 1) {
return;
}
queryStudiesWithProgress(studiesToExport)
.then(exportQueriedStudiesWithProgress);
};
const exportQueriedStudiesWithProgress = studiesToExport => {
const exportFilesCount = getNumberOfFilesToExport(studiesToExport);
let exportHandler;
return OHIF.ui.showDialog('dialogProgress', {
title: 'Exporting Studies...',
message: `Exported files: 0 / ${exportFilesCount}`,
total: getNumberOfFilesToExport(studiesToExport),
task: {
run: (dialog) => {
exportHandler = exportQueriedStudies(studiesToExport, {
notify: stats => {
const fileSize = convertSizeToString(stats.totalBytes);
dialog.update(stats.processed);
dialog.setMessage(`Exported files: ${stats.processed} / ${stats.total} (${fileSize})`);
}
});
exportHandler.promise.then(() => {
dialog.done();
}, () => {
dialog.setMessage('Failed to export studies');
});
}
}
}).then(null, err => {
exportHandler.cancel();
});
};
const exportQueriedStudies = (studiesToExport, options) => {
const zip = new JSZip(),
promises = [],
pendingDownloads = [],
exportFilesCount = getNumberOfFilesToExport(studiesToExport),
notify = (options || {}).notify || function() { /* noop */ };
const cancelDownloads = () => {
while(pendingDownloads.length) {
const download = pendingDownloads.pop();
download.cancel();
}
};
let totalBytes = 0;
studiesToExport.forEach(study => {
study.seriesList.forEach(series => {
series.instances.forEach(instance => {
if (!instance.wadouri) {
return;
}
const download = downloadDicomFile(instance);
pendingDownloads.push(download);
const promise = download.promise
.then(data => {
const downloadIndex = pendingDownloads.indexOf(download);
totalBytes += data && data.size ? data.size : 0;
if(downloadIndex > -1) {
pendingDownloads.splice(downloadIndex, 1);
}
notify({
total: exportFilesCount,
processed: exportFilesCount - pendingDownloads.length,
totalBytes: totalBytes
})
return zipInstance(study, series, instance, zip, data)
})
.catch(err => {
if(!(err instanceof ExportStudyDownloadCanceledError)) {
OHIF.log.error('Failed to export studies', err);
}
cancelDownloads();
throw err;
});
promises.push(promise);
});
});
});
return {
cancel: cancelDownloads,
promise: Promise.all(promises).then(() => {
const zipContent = zip.generate({ type: 'blob' });
saveAs(zipContent, 'studies.zip');
})
}
};
const downloadDicomFile = instance => {
let xhr,
promiseReject;
const promise = new Promise((resolve, reject) => {
promiseReject = reject;
xhr = new XMLHttpRequest();
xhr.open('GET', instance.wadouri, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.readyState === 4 && xhr.status !== 200) {
return reject(new Error(`File not downloaded: ${instance.wadouri}`));
}
resolve(xhr.response)
};
xhr.onerror = () => {
reject(new Error(`File not downloaded: ${instance.wadouri}`));
};
xhr.send();
});
return {
promise: promise,
cancel: () => {
xhr.abort();
promiseReject(new ExportStudyDownloadCanceledError('Download canceled'));
}
}
};
const zipInstance = (study, series, instance, zip, data) => {
const fileReader = new FileReader(),
blobFile = new Blob([data], { type: 'application/dicom' }),
zipFolder = zip.folder(study.studyInstanceUid).folder(series.seriesInstanceUid);
const promise = new Promise((resolve, reject) => {
fileReader.onload = () => {
try {
zipFolder.file(`${instance.sopInstanceUid}.dcm`, fileReader.result, { binary: true });
resolve()
} catch(err) {
reject(err);
}
};
});
fileReader.readAsArrayBuffer(blobFile);
return promise;
};
const ExportStudyDownloadCanceledError = (message) => {
this.name = 'ExportStudyDownloadCanceledError';
this.message = message || 'Download canceled';
this.stack = (new Error()).stack;
}
ExportStudyDownloadCanceledError.prototype = Object.create(Error.prototype);
ExportStudyDownloadCanceledError.prototype.constructor = ExportStudyDownloadCanceledError;