This solution get the image width/height without loading the image at all.
The trick here, is only load the first 1k of the image, where the metadata is placed, and then parse this info.
This code only works for .jpg images.
Need some adaptation for work with another formats.
function jpg1k(url) {
// indexOf for multi-pattern
function indexOfMulti(arr, pattern) {
var found,
_index = arr.indexOf(pattern[0]);
while (_index > -1) {
found = true;
var _idx = _index;
for (var patt of pattern) {
if (arr.indexOf(patt, _idx) !== _idx) {
found = false;
break;
}
_idx++;
}
if (found) {
return _index;
}
_index = arr.indexOf(pattern[0], _index + 1);
}
return -1;
}
const SOF_B = [ 0xFF, 0xC0 ], // Start Of Frame (Baseline),
SOF_P = [ 0xFF, 0xC2 ]; // Start Of Frame (Progressive)
return new Promise(function(res, rej) {
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return;
}
const JPG = new Uint8Array(xhr.response);
const IDX_SOF_B = indexOfMulti(JPG, SOF_B);
if (IDX_SOF_B > -1) {
var h = JPG.slice(IDX_SOF_B + 5, IDX_SOF_B + 7),
w = JPG.slice(IDX_SOF_B + 7, IDX_SOF_B + 9);
h = parseInt(h[0].toString(2) + h[1].toString(2).padStart(8, '0'), 2);
w = parseInt(w[0].toString(2) + w[1].toString(2).padStart(8, '0'), 2);
return res({ w: w, h: h });
}
const IDX_SOF_P = indexOfMulti(JPG, SOF_P);
if (IDX_SOF_P > -1) {
var h = JPG.slice(IDX_SOF_P + 5, IDX_SOF_P + 7),
w = JPG.slice(IDX_SOF_P + 7, IDX_SOF_P + 9);
h = parseInt(h[0].toString(2) + h[1].toString(2).padStart(8, '0'), 2);
w = parseInt(w[0].toString(2) + w[1].toString(2).padStart(8, '0'), 2);
return res({ w: w, h: h });
}
return rej({ w: -1, h: -1 });
};
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.setRequestHeader('Range', 'bytes=0-1024');
xhr.send(null);
});
}
jpg1k('path_to_your_image.jpg')
.then(console.log);
For PNG, we need adapt the code to PNG struct, but we only need the first 24 bytes to determine the width/height, so we dont need to request 1k like jpg.
function png24b(url) {
return new Promise(function(res, rej) {
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return;
}
const PNG = new Uint8Array(xhr.response),
decoder = new TextDecoder();
// PNG.slice(0, 8) === [ _ P N G CR LF _ _ ]
// PNG.slice(8, 16) === [ CHUNKLENGTH CHUNKFORMAT ]
// IHDR must be the first CHUNKFORMAT:
// PNG.slice(16, 24) === [ WIDTH------ HEIGHT----- ]
if ( decoder.decode(PNG.slice(1, 4)) === 'PNG' ) {
const view = new DataView(xhr.response);
return res({ w: view.getUint32(16), h: view.getUint32(20) });
}
return rej({ w: -1, h: -1 });
};
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.setRequestHeader('Range', 'bytes=0-24');
xhr.send(null);
});
}
png24b('path_to_your_image.png')
.then(console.log);