按图库中的图像分辨率排序

时间:2017-03-01 19:59:03

标签: javascript jquery html image

我刚刚创建了这个图库: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图像。

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

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

5 个答案:

答案 0 :(得分:4)

从理论上讲,通过从上传文件的arrayBuffer表示中提取这些值,可以利用浏览器的处理能力。

大多数图片格式都包含可读的元数据,其中包含媒体的尺寸,因此我们可以在不要求浏览器实际解析和计算图像数据(解压缩,解码等)的情况下访问它。

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



/* 
	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>';
}
&#13;
table {
  margin-top: 12px;
  border-collapse: collapse;
}

td,
th {
  border: 1px solid #000;
  padding: 2px 6px;
}

tr {
  border: 0;
  margin: 0;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

根据您的要求排序后,您可以使用此图像获取图像大小

这是我的示例:如何在加载https://jsfiddle.net/mmghori/Lsnc0sr7/

之前获取图像大小

这里是你更新的小提琴https://jsfiddle.net/mmghori/ubgLv3cb/,在这个小提琴我添加我的代码和控制台图像大小,

所以我只想找到如何在加载前获取图像大小。

<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);


        }

    });

答案 2 :(得分:0)

您应该分批处理图像,例如每次20个。

在图像加载时,您存储数组中每个图像的名称和分辨率,然后销毁图像以释放内存

当您获得20次加载或错误时,您将处理下一批。

完成所有操作后,您可以对数组进行排序并显示图像。

如果有数百或数千个,则需要对图库进行分页

它应该很简单,因为你已经拥有了所有图像名称和分辨率的数组。

只需设置一些变量来开始偏移和页面大小,并设置slice()数组以获得所需的子集(&#34;当前页面&#34;)

答案 3 :(得分:0)

  

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

这是一个简单的解决方案,如何获取网页上所有图像的大小,所以如果您有简单的照片库,从文件夹获取图像的内容以及您没有任何此图像列表,这是最简单的解决方案如何获得它的大小,然后你可以按照你想要的方式进行处理。

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>

答案 4 :(得分:0)

这样的事可以帮助吗?

var imgs = document.getElementsByClassName("imgs");

var height = imgs.clientHeight;
var width = imgs.clientWidth;

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

使用node.js:

imgs.sort(function(a, b) {
  return fs.statSync(a).size - fs.statSync(b).size;
});