在图库中按图像分辨率排序 [英] Sort by image resolution in gallery

查看:100
本文介绍了在图库中按图像分辨率排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我前一阵子创建了这个画廊: https://jsfiddle.net/5e9L09Ly/



不要担心它不会上传任何东西。



我让你可以按文件大小排序,但是我要按图像分辨率排序。问题是,并非所有图像都会加载到页面上,所以我不知道它们的大小。



图库本身非常基本,它会要求一个目录,然后它会显示该目录中的所有图像和视频。



它现在只能在chrome中使用,只需点击顶部的浏览。浏览前的数字是选择目录时显示或加载的图片数量。



我不知道如何解决这个问题...



我想到的一件事是这样的:

  imgLoad.attr( src,url); 
imgLoad.unbind(load);
imgLoad.bind(load,function(){
console.log(url +'size:'+(this.width + this.height));
});

然后,它会加载它加载的每一个图片,这会导致一个很大的压力在硬盘和浏览器上,如果你有很多图像,可以说20k图像,你正在尝试加载。



所以是的....任何建议都会很棒。



我不会在这里发布代码,因为它太多了,请查看小提琴。



大多数图像格式具有可读的元数据,其中包含媒体的维度,所以我们可以访问它,而不要求浏览器实际分析和计算图像的数据(解压缩,解码等)。

这是一个非常粗略的概念证明,使用 ExifReader lib 对jpeg图像没有太多的测试

/ *获取图像文件宽度&通过直接在arrayBuffer中读取它们的元数据,而不是加载它应该支持大多数jpeg png gif和bmp图像文件(尽管这些格式的所有版本都没有经过测试)@input一个fileList。 @output履约处理程序接收包含成功解析文件的数组的承诺* / function getImageSizes(files){/ *将指定大小的缓冲区附加到File对象* / function getBuffer(fileList,size){return new Promise (resolve,reject)=> {const fr = new FileReader(); const toLoad = fileList.length; if(!toLoad){//空列表resolve(fileList); return;} let arr = []; let加载= 0;让当前= fileList [加载];让大块= current.slice(0,大小|| current.size); / /只获得所需的字节fr.onload = e => {fileList [已加载]。 buf = fr.result; if(++ loaded< toLoad){current = fileList [loaded]; chunk = current.slice(0,size || current.size); fr.readAsArrayBuffer(chunk);} else { /一旦所有的列表已被处理resolve(fileList);}}; fr.readAsArrayBuffer(chunk);}); } / * png很容易,IHDR从16b开始,8个第一字节是32位宽度& height * / //你可以阅读https://www.w3.org/TR/PNG-Chunks.html以获得更多关于每个数值函数的信息getPNGSizes(pngArray){let view; // Little endian函数readInt16(offset){return view [offset]<< 24 |查看[offset + 1]<< 16 |查看[offset + 2]<< 8 |查看[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;});返回pngArray; }函数getJPEGSizes(jpegArray){/ * EXIF库似乎有一些困难* / let failed = [];让retry = [];让成功= []; // EXIF数据可以在文件中的任何位置,所以我们需要得到完整的数组缓冲区,返回getBuffer(jpegArray).then(jpegArray => {jpegArray.forEach(o => {try {const tags = ExifReader.load o.buf); if(!tags ||!tags.PixelXDimension){throw'no EXIF';} o.meta = tags; //因为OP说他想要它o.width = tags.PixelXDimension.value; o。 height = tags.PixelYDimension.value; success.push(o);} catch(e){failed.push(o); return;}}); //如果一些失败了,我们会用ol'good img重试((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);拒绝(O); }; img.src = URL.createObjectURL(o); }); });返回Promise.all(重试)//将exif的exif与exif的连接在一起。 (arr => success.concat(arr))}); }函数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;});返回gifArray; } function getBMPSizes(bmpArray){let view;返回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;});返回bmpArray; } //仅基于MIME类型的字符串,以避免所有非图像函数simpleImageFilter(files){return Promise.resolve(Array.prototype.filter.call(files,f => f.type.indexOf('image /')=== 0)); } function filterType(list,requestedType){//更强大的MIME类型检查//请参阅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){case89504e47 :case0D0A1A0A:type =image / png;打破;情况47494638:type =image / gif;打破; caseffd8ffe0:caseffd8ffe1:caseffd8ffe2:type =image / jpeg; caseffd8ffe2打破;默认:开关(header.substr(0,4)){case424d:type ='image / bmp';打破; } break; }返回类型; } return Array.prototype.filter.call(list,o => getHeader(o.buf)=== requestedType); } 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)));}) ; (返回simpleImageFilter(files).then(images => getBuffer(images,30)).then(getSizes);} //我们的回调函数sort(arr){arr.sort(function(a,b){return a。宽* a.height - b.width * b.height;}); output.innerHTML =''; arr.forEach(f => {//丑陋表格生成让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>正在处理中,请稍等...< / 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 =fileid =fwebkitdirectory accepting =image / *> ;<表> < THEAD> < TR> < th>文件名< / th> <的第i;宽度< /第> <的第i;高度< /第> < / TR> < / THEAD> < tbody id =输出> < TR> < td colspan =3>请选择要上传的文件夹< / td> < / TR> < / tbody>< / table>


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. 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.

解决方案

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>

这篇关于在图库中按图像分辨率排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆