I made this gallery a while ago: /
Don't worry it won't upload anything.
I made that you can sort by file size, but I want to sort by image resolution. The problem is that not all of the images will have loaded onto the page yet so I won't know what their size will be.
The gallery itself is pretty basic, it asks for a directory and it will then display all the images and videos in that directory.
It only works in chrome right now, just click on browse at the very top. The number before browse is how many images should show or load when you select the directory.
I am not sure how to approach this problem...
One thing I had in mind was something like this:
imgLoad.attr("src", url);
imgLoad.unbind("load");
imgLoad.bind("load", function() {
console.log(url+' size: '+(this.width + this.height));
});
The problem with that is then it loads each and every image it loads and it would put a big strain on the hdd and browser if you have a lot of images, lets say 20k images you are trying to load.
So yeah.... any suggestions would be great.
I won't post the code here because it is too much, please check out the fiddle.
I made this gallery a while ago: https://jsfiddle/5e9L09Ly/
Don't worry it won't upload anything.
I made that you can sort by file size, but I want to sort by image resolution. The problem is that not all of the images will have loaded onto the page yet so I won't know what their size will be.
The gallery itself is pretty basic, it asks for a directory and it will then display all the images and videos in that directory.
It only works in chrome right now, just click on browse at the very top. The number before browse is how many images should show or load when you select the directory.
I am not sure how to approach this problem...
One thing I had in mind was something like this:
imgLoad.attr("src", url);
imgLoad.unbind("load");
imgLoad.bind("load", function() {
console.log(url+' size: '+(this.width + this.height));
});
The problem with that is then it loads each and every image it loads and it would put a big strain on the hdd and browser if you have a lot of images, lets say 20k images you are trying to load.
So yeah.... any suggestions would be great.
I won't post the code here because it is too much, please check out the fiddle.
Share Improve this question asked Mar 1, 2017 at 19:59 ChrisChris 9977 silver badges24 bronze badges 5-
1
any suggestions would be great
-> cache – Marcin Orlowski Commented Mar 1, 2017 at 20:01 - @MarcinOrlowski Thanks, I haven't thought of that – Chris Commented Mar 1, 2017 at 20:01
- @K3N Thanks, I didn't know documentfragment existed until I googled it now. Seems really cool. I was thinking of using a webworker to slowly load the images into memory and read the width and height and insert the data into my main array and letting me know when it is done... but I am not sure – Chris Commented Mar 1, 2017 at 20:09
- Where are the images from? If you control them, can't you add the height/width into the img tags, and use any of a number of 'lazy load' functions to delay loading the actual images? – sideroxylon Commented Mar 4, 2017 at 6:48
- @sideroxylon Try the jsfiddle. It pulls it from the folder you select, I don't have access to that kind of metadata, I need to somehow calculate the width and height afterwards – Chris Commented Mar 4, 2017 at 6:58
5 Answers
Reset to default 4 +200Theoretically, 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 pute the image's data (unpress, 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/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],
pressionMethod: 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./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./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>
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/mmghori/Lsnc0sr7/
and
here is you updated fiddle https://jsfiddle/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);
}
});
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")
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/TR/html4/strict.dtd">
<html>
<head>
<title>Get image size example</title>
</head>
<body>
<img src="https://upload.wikimedia/wikipedia/mons/thumb/f/ff/Solid_blue.svg/225px-Solid_blue.svg.png"/>
<img src="http://images.mentalfloss./sites/default/files/styles/insert_main_wide_image/public/46346365365.png"/>
</body>
</html>
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;
});