是否可以使用FileReader API创建缩略图?

时间:2018-09-17 21:20:15

标签: javascript html html5

我已经有了用FileReader.readAsDataURL()加载多张图像所需的概念验证。

但是,我的客户的工作流程使得他们一次加载数百张图像,这使浏览器崩溃。

有什么方法可以将这些图像加载为实际的缩略图(16k与16Mb)?

1 个答案:

答案 0 :(得分:0)

首先,完全不要使用FileReader。

相反,要显示Blob中的任何数据,请使用URL.createObjectURL方法。
FileReader将三次将二进制数据加载到内存中(一次读取Blob进行转换时,一次加载为Base64 String,另一次通过 HTMLElement的src。)
如果文件存储在用户磁盘上,则blobURL将仅从HTMLElement中将数据加载到内存中一次。实际上,blobURL是指向Blob数据的直接指针。

因此,这已经为您释放了很多内存。

inp.onchange = e => {
  for(let file of inp.files) {
    let url = URL.createObjectURL(file);
    let img = new Image(200);
    img.src = url;
    document.body.appendChild(img);
  }
};
    
<input type="file" webkitdirectory accepts="image/*" id="inp">

现在,如果不足以生成缩略图,则可以在画布上绘制所有这些图像,并在需要时减小其大小。 但是请记住,要做到这一点,您无论如何都必须首先加载原始图像的数据,并且您不确定如何浏览器将清理此已用内存。因此,通过创建这些缩略图版本可能带来的危害大于任何事情。

无论如何,这是一个如何实现的基本示例:

inp.onchange = e => {
  Promise.all([...inp.files].map(toThumbnail))
    .then(imgs => document.body.append.apply(document.body, imgs.filter(v => v)))
    .catch(console.error);
};

function toThumbnail(file) {
  return loadImage(file)
    .then(drawToCanvas)
    .catch(_ => {});
}

function loadImage(file) {
  const url = URL.createObjectURL(file);
  const img = new Image();
  return new Promise((res, rej) => {
    img.onload = e => res(img);
    img.onerror = rej;
    img.src = url;
  });
};

function drawToCanvas(img) {
  const w = Math.min(img.naturalWidth, 200);
  const r = w / img.naturalWidth;
  const h = img.naturalHeight * r;
  const canvas = Object.assign(
    document.createElement('canvas'), {
      width: w,
      height: h
    }
  );
  canvas.getContext('2d').drawImage(img, 0, 0, w, h);
  return canvas;
}
<input type="file" webkitdirectory accepts="image/*" id="inp">