How can I validate file signatures before accessing them in a constructor? When I run the following code, the code after try/catch in the constructor get executed even if there is an exception from checkBlobSignature()
. I guess I should use async
/await
or callback functions, but any tip to make it easier for me?
class ImageEditor {
constructor(blobs) {
try {
for (const blob of blobs) {
checkBlobSignature(blob);
}
} catch(e) {
throw e.message;
}
/*
* Rest of my constructor code that I want to be executed
* if the for loop is finished, i.e. there are no exceptions.
*/
}
checkBlobSignature(blob) {
let fileReader = new FileReader();
fileReader.addEventListener("loadend", this.check.bind(this));
fileReader.readAsArrayBuffer(blob.slice(0, 8));
}
check(e) {
let uint8Array = new Uint8Array(e.target.result);
let signature = "";
for(const uint8 of uint8Array) {
signature = uint8.toString(16);
}
signature = signature.toUpperCase();
switch (true) {
case /^89504E470D0A1A0A/.test(signature): // PNG Hex signature
break;
case /^474946383761/.test(signature): // GIF Hex signature
case /^474946383961/.test(signature): // GIF Hex signature
break;
case /^FFD8FF/.test(signature): // JPEG Hex signature
break;
case /^6674797069736F6D/.test(signature): // MP4 HEX signature
break;
default:
throw "Invalid signature.";
}
}
}
CodePudding user response:
Constructors can't be asynchronous, but you can define an asynchronous static method to construct an instance of the class.
Secondly, you can use Blob.prototype.arrayBuffer()
instead of a FileReader
to get a Uint8Array
from an ArrayBuffer
promise. It is also possible to do so with a FileReader
, but requires the explicit promise construction antipattern, so I don't recommend it.
Lastly, since checkBlobSignature
doesn't use this
, you can also make that a static method.
class ImageEditor {
static async from(blobs) {
await Promise.all(Array.from(blobs, ImageEditor.checkBlobSignature));
return new ImageEditor(blobs);
}
static async checkBlobSignature(blob) {
const signatures = [
[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], // PNG
[0x47, 0x49, 0x46, 0x38, 0x37, 0x61], // GIF
[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], // GIF
[0xFF, 0xD8, 0xFF], // JPEG
[0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D], // MP4
];
const maxSignatureLength = Math.max(
...signatures.map(signature => signature.length),
);
const arrayBuffer = await blob.slice(0, maxSignatureLength).arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
if (!signatures.some(
signature => signature.every((byte, index) => byte === uint8Array[index]),
)) {
throw new Error('Invalid signature');
}
}
ImageEditor(blobs) {
/* Rest of my constructor code that I want to be executed
* if the for loop is finished, i.e. there are no exceptions.
*/
}
}