mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-02 22:21:01 +01:00
Add new worker for zipping outputs.
Use bakeId to track which outputs are stale.
This commit is contained in:
parent
df20196201
commit
07021b8dd5
@ -93,7 +93,6 @@ self.addEventListener("message", function(e) {
|
||||
async function bake(data) {
|
||||
// Ensure the relevant modules are loaded
|
||||
self.loadRequiredModules(data.recipeConfig);
|
||||
|
||||
try {
|
||||
self.inputNum = parseInt(data.inputNum, 10);
|
||||
const response = await self.chef.bake(
|
||||
@ -119,7 +118,8 @@ async function bake(data) {
|
||||
action: "bakeComplete",
|
||||
data: Object.assign(response, {
|
||||
id: data.id,
|
||||
inputNum: data.inputNum
|
||||
inputNum: data.inputNum,
|
||||
bakeId: data.bakeId
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -128,7 +128,8 @@ async function bake(data) {
|
||||
action: "bakeComplete",
|
||||
data: Object.assign(response, {
|
||||
id: data.id,
|
||||
inputNum: data.inputNum
|
||||
inputNum: data.inputNum,
|
||||
bakeId: data.bakeId
|
||||
})
|
||||
}, [response.result]);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Utils from "../core/Utils";
|
||||
import FileSaver from "file-saver";
|
||||
import zip from "zlibjs/bin/zip.min";
|
||||
import ZipWorker from "worker-loader?inline&fallback=false!./ZipWorker";
|
||||
|
||||
const Zlib = zip.Zlib;
|
||||
|
||||
@ -29,6 +30,8 @@ class OutputWaiter {
|
||||
this.outputs = {};
|
||||
this.activeTab = -1;
|
||||
|
||||
this.zipWorker = null;
|
||||
|
||||
this.maxTabs = 4; // Calculate this
|
||||
}
|
||||
|
||||
@ -85,7 +88,8 @@ class OutputWaiter {
|
||||
inputNum: inputNum,
|
||||
statusMessage: `Input ${inputNum} has not been baked yet.`,
|
||||
error: null,
|
||||
status: "inactive"
|
||||
status: "inactive",
|
||||
bakeId: -1
|
||||
};
|
||||
|
||||
this.outputs[inputNum] = newOutput;
|
||||
@ -163,6 +167,17 @@ class OutputWaiter {
|
||||
this.set(inputNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stored bake ID for the output in the ouptut array
|
||||
*
|
||||
* @param {number} bakeId
|
||||
* @param {number} inputNum
|
||||
*/
|
||||
updateOutputBakeId(bakeId, inputNum) {
|
||||
if (this.getOutput(inputNum) === -1) return;
|
||||
this.outputs[inputNum].bakeId = bakeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an output from the output array.
|
||||
*
|
||||
@ -202,11 +217,13 @@ class OutputWaiter {
|
||||
const outputFile = document.getElementById("output-file");
|
||||
const outputHighlighter = document.getElementById("output-highlighter");
|
||||
const inputHighlighter = document.getElementById("input-highlighter");
|
||||
// If inactive, show blank
|
||||
// If pending or baking, show loader and status message
|
||||
// If error, style the tab and handle the error
|
||||
// If done, display the output if it's the active tab
|
||||
if (output.status === "inactive" || output.status === "stale") {
|
||||
// If inactive, show the last bake value (or blank)
|
||||
if (output.status === "inactive" ||
|
||||
output.status === "stale" ||
|
||||
(output.status === "baked" && output.bakeId < this.manager.worker.bakeId)) {
|
||||
this.manager.controls.showStaleIndicator();
|
||||
} else {
|
||||
this.manager.controls.hideStaleIndicator();
|
||||
@ -421,10 +438,111 @@ class OutputWaiter {
|
||||
this.downloadAllFiles();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawns a new ZipWorker and sends it the outputs so that they can
|
||||
* be zipped for download
|
||||
*/
|
||||
downloadAllFiles() {
|
||||
|
||||
const inputNums = Object.keys(this.outputs);
|
||||
for (let i = 0; i < inputNums.length; i++) {
|
||||
const iNum = inputNums[i];
|
||||
if (this.outputs[iNum].status !== "baked" ||
|
||||
this.outputs[iNum].bakeId !== this.manager.worker.bakeId) {
|
||||
if (window.confirm("Not all outputs have been baked yet. Continue downloading outputs?")) {
|
||||
break;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = window.prompt("Please enter a filename: ", "download.zip");
|
||||
|
||||
if (fileName === null || fileName === "") {
|
||||
// Don't zip the files if there isn't a filename
|
||||
this.app.alert("No filename was specified.", 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
let fileExt = window.prompt("Please enter a file extension for the files: ", ".txt");
|
||||
|
||||
if (fileExt === null) {
|
||||
// Use .dat as the default file extension
|
||||
fileExt = ".dat";
|
||||
}
|
||||
|
||||
if (this.zipWorker !== null) {
|
||||
this.terminateZipWorker();
|
||||
}
|
||||
|
||||
const downloadButton = document.getElementById("save-all-to-file");
|
||||
|
||||
downloadButton.disabled = true;
|
||||
downloadButton.classList.add("spin");
|
||||
$("[data-toggle='tooltip']").tooltip("hide");
|
||||
|
||||
downloadButton.firstElementChild.innerHTML = "autorenew";
|
||||
|
||||
log.debug("Creating ZipWorker");
|
||||
this.zipWorker = new ZipWorker();
|
||||
this.zipWorker.postMessage({
|
||||
outputs: this.outputs,
|
||||
filename: fileName,
|
||||
fileExtension: fileExt
|
||||
});
|
||||
this.zipWorker.addEventListener("message", this.handleZipWorkerMessage.bind(this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the ZipWorker
|
||||
*/
|
||||
terminateZipWorker() {
|
||||
if (this.zipWorker === null) return; // Already terminated
|
||||
|
||||
log.debug("Terminating ZipWorker.");
|
||||
|
||||
this.zipWorker.terminate();
|
||||
this.zipWorker = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle messages sent back by the ZipWorker
|
||||
*/
|
||||
handleZipWorkerMessage(e) {
|
||||
const r = e.data;
|
||||
if (!r.hasOwnProperty("zippedFile")) {
|
||||
log.error("No zipped file was sent in the message.");
|
||||
this.terminateZipWorker();
|
||||
return;
|
||||
}
|
||||
if (!r.hasOwnProperty("filename")) {
|
||||
log.error("No filename was sent in the message.");
|
||||
this.terminateZipWorker();
|
||||
return;
|
||||
}
|
||||
|
||||
const file = new File([r.zippedFile], r.filename);
|
||||
FileSaver.saveAs(file, r.filename, false);
|
||||
|
||||
this.terminateZipWorker();
|
||||
|
||||
const downloadButton = document.getElementById("save-all-to-file");
|
||||
|
||||
downloadButton.disabled = false;
|
||||
downloadButton.classList.remove("spin");
|
||||
|
||||
downloadButton.firstElementChild.innerHTML = "archive";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for download all files events.
|
||||
*/
|
||||
async downloadAllFiles() {
|
||||
async downloadAllFilesOld() {
|
||||
const fileName = window.prompt("Please enter a filename: ", "download.zip");
|
||||
const fileExt = window.prompt("Please enter a file extension for the files: ", ".txt");
|
||||
const zip = new Zlib.Zip();
|
||||
@ -931,7 +1049,11 @@ class OutputWaiter {
|
||||
* Copies the output to the clipboard
|
||||
*/
|
||||
copyClick() {
|
||||
const output = this.getActive();
|
||||
let output = this.getActive();
|
||||
|
||||
if (typeof output !== "string") {
|
||||
output = Utils.arrayBufferToStr(output);
|
||||
}
|
||||
|
||||
// Create invisible textarea to populate with the raw dish string (not the printable version that
|
||||
// contains dots instead of the actual bytes)
|
||||
|
@ -26,6 +26,7 @@ class WorkerWaiter {
|
||||
this.maxWorkers = navigator.hardwareConcurrency || 4;
|
||||
this.inputs = [];
|
||||
this.totalOutputs = 0;
|
||||
this.bakeId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,7 +142,7 @@ class WorkerWaiter {
|
||||
switch (r.action) {
|
||||
case "bakeComplete":
|
||||
log.debug(`Bake ${inputNum} complete.`);
|
||||
this.updateOutput(r.data, r.data.inputNum);
|
||||
this.updateOutput(r.data, r.data.inputNum, r.data.bakeId);
|
||||
this.workerFinished(currentWorker);
|
||||
break;
|
||||
case "bakeError":
|
||||
@ -187,11 +188,12 @@ class WorkerWaiter {
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {number} inputNum
|
||||
* @param {number} bakeId
|
||||
*/
|
||||
updateOutput(data, inputNum) {
|
||||
updateOutput(data, inputNum, bakeId) {
|
||||
this.manager.output.updateOutputBakeId(bakeId, inputNum);
|
||||
this.manager.output.updateOutputValue(data, inputNum);
|
||||
this.manager.output.updateOutputStatus("baked", inputNum);
|
||||
|
||||
// this.manager.recipe.updateBreakpointIndicator(this.app.progress);
|
||||
}
|
||||
|
||||
@ -337,7 +339,8 @@ class WorkerWaiter {
|
||||
options: this.options,
|
||||
progress: this.progress,
|
||||
step: this.step,
|
||||
inputNum: nextInput.inputNum
|
||||
inputNum: nextInput.inputNum,
|
||||
bakeId: this.bakeId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -349,7 +352,8 @@ class WorkerWaiter {
|
||||
options: this.options,
|
||||
progress: this.progress,
|
||||
step: this.step,
|
||||
inputNum: nextInput.inputNum
|
||||
inputNum: nextInput.inputNum,
|
||||
bakeId: this.bakeId
|
||||
}
|
||||
}, [nextInput.input]);
|
||||
}
|
||||
@ -366,7 +370,7 @@ class WorkerWaiter {
|
||||
bake(recipeConfig, options, progress, step) {
|
||||
this.setBakingStatus(true);
|
||||
this.bakeStartTime = new Date().getTime();
|
||||
|
||||
this.bakeId++;
|
||||
this.recipeConfig = recipeConfig;
|
||||
this.options = options;
|
||||
this.progress = progress;
|
||||
|
69
src/web/ZipWorker.mjs
Normal file
69
src/web/ZipWorker.mjs
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Web Worker to handle zipping the outputs for download.
|
||||
*
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import zip from "zlibjs/bin/zip.min";
|
||||
import Utils from "../core/Utils";
|
||||
|
||||
const Zlib = zip.Zlib;
|
||||
|
||||
/**
|
||||
* Respond to message from parent thread.
|
||||
*/
|
||||
self.addEventListener("message", function(e) {
|
||||
const r = e.data;
|
||||
if (!r.hasOwnProperty("outputs")) {
|
||||
log.error("No files were passed to the ZipWorker.");
|
||||
return;
|
||||
}
|
||||
if (!r.hasOwnProperty("filename")) {
|
||||
log.error("No filename was passed to the ZipWorker");
|
||||
return;
|
||||
}
|
||||
if (!r.hasOwnProperty("fileExtension")) {
|
||||
log.error("No file extension was passed to the ZipWorker");
|
||||
return;
|
||||
}
|
||||
|
||||
self.zipFiles(r.outputs, r.filename, r.fileExtension);
|
||||
});
|
||||
|
||||
self.setOption = function(...args) {};
|
||||
|
||||
/**
|
||||
* Compress the files into a zip file and send the zip back
|
||||
* to the OutputWaiter.
|
||||
*
|
||||
* @param {object} outputs
|
||||
* @param {string} filename
|
||||
* @param {string} fileExtension
|
||||
*/
|
||||
self.zipFiles = function(outputs, filename, fileExtension) {
|
||||
const zip = new Zlib.Zip();
|
||||
const inputNums = Object.keys(outputs);
|
||||
|
||||
for (let i = 0; i < inputNums.length; i++) {
|
||||
const iNum = inputNums[i];
|
||||
const name = Utils.strToByteArray(iNum + fileExtension);
|
||||
|
||||
let output;
|
||||
if (outputs[iNum].data === null) {
|
||||
output = new Uint8Array(0);
|
||||
} else if (typeof outputs[iNum].data.result === "string") {
|
||||
output = new Uint8Array(Utils.strToArrayBuffer(outputs[iNum].data.result));
|
||||
} else {
|
||||
output = new Uint8Array(outputs[iNum].data.result);
|
||||
}
|
||||
zip.addFile(output, {filename: name});
|
||||
}
|
||||
|
||||
const zippedFile = zip.compress();
|
||||
self.postMessage({
|
||||
zippedFile: zippedFile.buffer,
|
||||
filename: filename
|
||||
}, [zippedFile.buffer]);
|
||||
};
|
@ -49,3 +49,15 @@
|
||||
#controls .btn {
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation-name: spin;
|
||||
animation-duration: 3s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {transform: rotate(0deg);}
|
||||
100% {transform: rotate(360deg);}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user