Skip to content Skip to sidebar Skip to footer

Sort By Image Resolution In Gallery

I made this gallery a while ago: https://jsfiddle.net/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.

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 libthat 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.
*/functiongetImageSizes(files) {
  /* Attaches a buffer of the size specified to the File object */functiongetBuffer(fileList, size) {

    returnnewPromise((resolve, reject) => {

      const fr = newFileReader();
      const toLoad = fileList.length;
      if (!toLoad) { // an empty listresolve(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 treatedresolve(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 valuefunctiongetPNGSizes(pngArray) {
    let view;
    // Little endian onlyfunctionreadInt16(offset) {
      return view[offset] << 24 |
        view[offset + 1] << 16 |
        view[offset + 2] << 8 |
        view[offset + 3];
    }

    pngArray.forEach(o => {
      view = newUint8Array(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;
  }

  functiongetJPEGSizes(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 arrayBufferreturngetBuffer(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) => {
        returnnewPromise((resolve, reject) => {
          let img = newImage();
          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);
        });
      });

      returnPromise.all(retry)
        // concatenate the no-exif ones with the exif ones.
        .then(arr => success.concat(arr))
    });
  }

  functiongetGIFSizes(gifArray) {
    gifArray.forEach(o => {
      let view = newUint8Array(o.buf);
      o.width = view[6] | view[7] << 8;
      o.height = view[8] | view[9] << 8;
    });
    return gifArray;
  }

  functiongetBMPSizes(bmpArray) {
    let view;

    functionreadInt(offset) {
      // I probably have something wrong in here...returnMath.abs(view[offset] |
        view[offset + 1] << 8 |
        view[offset + 2] << 16 |
        view[offset + 3] << 24
      );
    }
    bmpArray.forEach(o => {
      view = newUint8Array(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-imagesfunctionsimpleImageFilter(files) {
    returnPromise.resolve(
      Array.prototype.filter.call(files, f => f.type.indexOf('image/') === 0)
    );
  }

  functionfilterType(list, requestedType) {
    // A more robust MIME-type check// see http://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-uploadfunctiongetHeader(buf) {
      let type = 'unknown';
      let header = Array.prototype.map.call(
        newUint8Array(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;
    }

    returnArray.prototype.filter.call(
      list,
      o =>getHeader(o.buf) === requestedType
    );

  }

  functiongetSizes(fileArray) {
    returngetJPEGSizes(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)));
      });
  }

  returnsimpleImageFilter(files)
    .then(images =>getBuffer(images, 30))
    .then(getSizes);
}


// our callbackfunctionsort(arr) {

  arr.sort(function(a, b) {
    return a.width * a.height - b.width * b.height;
  });

  output.innerHTML = '';
  arr.forEach(f => {
    // ugly table generationlet 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: 2px6px;
}

tr {
  border: 0;
  margin: 0;
}
<scriptsrc="https://rawgit.com/mattiasw/ExifReader/master/dist/exif-reader.js"></script><inputtype="file"id="f"webkitdirectoryaccepts="image/*"><table><thead><tr><th>file name</th><th>width</th><th>height</th></tr></thead><tbodyid="output"><tr><tdcolspan="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" />

<h4id="fileSize"></h4>var _URL = window.URL || window.webkitURL;

    $("#file").change(function(e) {
        var file, img;


        if ((file = this.files[0])) {
            img = newImage();
            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 HTMLPUBLIC"-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><html><head><title>Get image size example</title></head><body><imgsrc="https://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Solid_blue.svg/225px-Solid_blue.svg.png"/><imgsrc="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"