This method retrieves the image width/height without actually loading the entire image.
The approach here involves loading only the first 1k of the image, where the metadata is stored, and then extracting this information.
This functionality currently supports only .jpg images and may require adjustments to work with other formats.
function jpg1k(url) {
// indexOf function for multiple patterns
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);
To handle PNG files, some modifications are needed in the code to account for PNG file structure. However, only the first 24 bytes of the PNG file are required to determine the width/height, thus eliminating the need to fetch the first 1k as done with jpg files.
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);