我已经有了用FileReader.readAsDataURL()加载多张图像所需的概念验证。
但是,我的客户的工作流程使得他们一次加载数百张图像,这使浏览器崩溃。
有什么方法可以将这些图像加载为实际的缩略图(16k与16Mb)?
答案 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">