Sort By Image Resolution In Gallery
Solution 1:
Theoretically, there would be way to leverage a bit the browser's processing, by extracting these values from arrayBuffer representations of the uploaded files.
Most image formats have readable metadata containing the dimensions of the media, so we can access it without asking the browser to actually parse and compute the image's data (uncompress, decode etc.).
Here is a really rough proof of concept, using ExifReader lib that I don't have tested too much, for jpeg images.
/*
Rough proof of concept of getting image files width & height
by reading their metadata directly in arrayBuffer, instead of loading it
Should support most jpeg png gif and bmp image files
(though all versions of these formats have NOT been tested)
@input A fileList.
@output A promise
whose fulfillment handler receives an Array containing successfully parsed files.
*/
function getImageSizes(files) {
/* Attaches a buffer of the size specified to the File object */
function getBuffer(fileList, size) {
return new Promise((resolve, reject) => {
const fr = new FileReader();
const toLoad = fileList.length;
if (!toLoad) { // an empty list
resolve(fileList);
return;
}
let arr = [];
let loaded = 0;
let current = fileList[loaded];
let chunk = current.slice(0, size || current.size); // get only the required bytes
fr.onload = e => {
fileList[loaded].buf = fr.result;
if (++loaded < toLoad) {
current = fileList[loaded];
chunk = current.slice(0, size || current.size);
fr.readAsArrayBuffer(chunk);
} else { // once all the list has been treated
resolve(fileList);
}
};
fr.readAsArrayBuffer(chunk);
});
}
/* png is easy, IHDR starts at 16b, and 8 first bytes are 32bit width & height */
// You can read https://www.w3.org/TR/PNG-Chunks.html for more info on each numeric value
function getPNGSizes(pngArray) {
let view;
// Little endian only
function readInt16(offset) {
return view[offset] << 24 |
view[offset + 1] << 16 |
view[offset + 2] << 8 |
view[offset + 3];
}
pngArray.forEach(o => {
view = new Uint8Array(o.buf);
o.meta = {
width: readInt16(16),
height: readInt16(20),
bitDepth: view[24],
colorType: view[25],
compressionMethod: view[26],
filterMethod: view[27],
interlaceMethod: view[28]
};
o.width = o.meta.width;
o.height = o.meta.height;
});
return pngArray;
}
function getJPEGSizes(jpegArray) {
/* the EXIF library seems to have some difficulties */
let failed = [];
let retry = [];
let success = [];
// EXIF data can be anywhere in the file, so we need to get the full arrayBuffer
return getBuffer(jpegArray).then(jpegArray => {
jpegArray.forEach(o => {
try {
const tags = ExifReader.load(o.buf);
if (!tags || !tags.PixelXDimension) {
throw 'no EXIF';
}
o.meta = tags; // since OP said he wanted it
o.width = tags.PixelXDimension.value;
o.height = tags.PixelYDimension.value;
success.push(o);
} catch (e) {
failed.push(o);
return;
}
});
// if some have failed, we will retry with the ol'good img way
retry = failed.map((o) => {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = e => {
URL.revokeObjectURL(img.src);
o.width = img.width;
o.height = img.height;
resolve(o);
};
img.onerror = e => {
URL.revokeObjectURL(img.src);
reject(o);
};
img.src = URL.createObjectURL(o);
});
});
return Promise.all(retry)
// concatenate the no-exif ones with the exif ones.
.then(arr => success.concat(arr))
});
}
function getGIFSizes(gifArray) {
gifArray.forEach(o => {
let view = new Uint8Array(o.buf);
o.width = view[6] | view[7] << 8;
o.height = view[8] | view[9] << 8;
});
return gifArray;
}
function getBMPSizes(bmpArray) {
let view;
function readInt(offset) {
// I probably have something wrong in here...
return Math.abs(view[offset] |
view[offset + 1] << 8 |
view[offset + 2] << 16 |
view[offset + 3] << 24
);
}
bmpArray.forEach(o => {
view = new Uint8Array(o.buf);
o.meta = {
width: readInt(18),
height: readInt(22)
}
o.width = o.meta.width;
o.height = o.meta.height;
});
return bmpArray;
}
// only based on MIME-type string, to avoid all non-images
function simpleImageFilter(files) {
return Promise.resolve(
Array.prototype.filter.call(files, f => f.type.indexOf('image/') === 0)
);
}
function filterType(list, requestedType) {
// A more robust MIME-type check
// see http://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-upload
function getHeader(buf) {
let type = 'unknown';
let header = Array.prototype.map.call(
new Uint8Array(buf.slice(0, 4)),
v => v.toString(16)
).join('')
switch (header) {
case "89504e47":
case "0D0A1A0A":
type = "image/png";
break;
case "47494638":
type = "image/gif";
break;
case "ffd8ffe0":
case "ffd8ffe1":
case "ffd8ffe2":
type = "image/jpeg";
break;
default:
switch (header.substr(0, 4)) {
case "424d":
type = 'image/bmp';
break;
}
break;
}
return type;
}
return Array.prototype.filter.call(
list,
o => getHeader(o.buf) === requestedType
);
}
function getSizes(fileArray) {
return getJPEGSizes(filterType(fileArray, 'image/jpeg'))
.then(jpegs => {
let pngs = getPNGSizes(filterType(fileArray, 'image/png'));
let gifs = getGIFSizes(filterType(fileArray, 'image/gif'));
let bmps = getBMPSizes(filterType(fileArray, 'image/bmp'));
return gifs.concat(pngs.concat(bmps.concat(jpegs)));
});
}
return simpleImageFilter(files)
.then(images => getBuffer(images, 30))
.then(getSizes);
}
// our callback
function sort(arr) {
arr.sort(function(a, b) {
return a.width * a.height - b.width * b.height;
});
output.innerHTML = '';
arr.forEach(f => {
// ugly table generation
let t = '<td>',
tt = '</td>' + t,
ttt = '</td></tr>';
output.innerHTML += '<tr>' + t + f.name + tt + f.width + tt + f.height + ttt;
})
}
f.onchange = e => {
getImageSizes(f.files)
.then(sort)
.catch(e => console.log(e));
output.innerHTML = '<tr><td colspan="3">Processing, please wait...</td></tr>';
}
table {
margin-top: 12px;
border-collapse: collapse;
}
td,
th {
border: 1px solid #000;
padding: 2px 6px;
}
tr {
border: 0;
margin: 0;
}
<script src="https://rawgit.com/mattiasw/ExifReader/master/dist/exif-reader.js"></script>
<input type="file" id="f" webkitdirectory accepts="image/*">
<table>
<thead>
<tr>
<th>file name</th>
<th>width</th>
<th>height</th>
</tr>
</thead>
<tbody id="output">
<tr>
<td colspan="3">Please choose a folder to upload</td>
</tr>
</tbody>
</table>
Solution 2:
you can get the image size using this an after you sort as per your requirement
this is my example : how to get image size before load https://jsfiddle.net/mmghori/Lsnc0sr7/
and
here is you updated fiddle https://jsfiddle.net/mmghori/ubgLv3cb/ , in this fiddle i add my code and console image size,
so i just find how to get image size before load.
<input type="file" id="file" />
<h4 id="fileSize"></h4>
var _URL = window.URL || window.webkitURL;
$("#file").change(function(e) {
var file, img;
if ((file = this.files[0])) {
img = new Image();
img.onload = function() {
$("#fileSize").text(this.width + "x" + this.height);
};
img.onerror = function() {
alert( "not a valid file: " + file.type);
};
img.src = _URL.createObjectURL(file);
}
});
Solution 3:
You should process images in batches, say 20 at a time.
On image load you store name and resolution for each of the images in an array, then destroy the images to free up memory
When you get the 20 onload or onerror, you process the next batch.
When all is done, you sort the array and display the images .
If there are hundreds or thousands of them, you need to paginate the gallery
It should be simple, since you already have the array with all image names and resolutions at hand.
just set some variables for starting offset and page size and slice() the array to get the subset you need (the "current page")
Solution 4:
The gallery itself is pretty basic, it asks for a directory and it will then display all the images and videos in that directory.
Here is simple solution, how to get size of all images on webpage, so if you have something like simple photo-gallery, what getting images from folder and you haven't got any list of this images, this is the simplest solution how to get size of that and then you can process in how you want.
for( i=0; i < document.images.length; i++)
{
width = document.images[i].width;
height = document.images[i].height;
console.log("Image number: " + i + " Size: " + width + "x" + height);
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Get image size example</title>
</head>
<body>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Solid_blue.svg/225px-Solid_blue.svg.png"/>
<img src="http://images.mentalfloss.com/sites/default/files/styles/insert_main_wide_image/public/46346365365.png"/>
</body>
</html>
Solution 5:
Could something like this help?
var imgs = document.getElementsByClassName("imgs");
var height = imgs.clientHeight;
var width = imgs.clientWidth;
imgs.sort(function(a, b){
return a.height - b.width;
});
or using node.js:
imgs.sort(function(a, b) {
return fs.statSync(a).size - fs.statSync(b).size;
});
Post a Comment for "Sort By Image Resolution In Gallery"