HTML5上传前预先调整图像的大小

这是一个面条刮刀。

请记住,我们有HTML5本地存储和xhr v2,什么不是。 我想知道是否有人可以find一个工作的例子,甚至只是给我一个是或否的这个问题:

是否有可能预先使用新的本地存储(或其他)图像的大小,以便一个用户谁不知道调整图像的大小,可以拖动他们的10MB图像到我的网站,resize使用新的本地存储和那么以较小的尺寸上传它。

我完全知道你可以用Flash,Java applets,active X来做…问题是如果你可以用Javascript + Html5做。

期待在这一个回应。

Ta现在。

是的,使用File API ,然后您可以使用canvas元素处理图像。

这个Mozilla Hacks博客文章引导你完成大部分的过程。 以下是博客文章中汇编的源代码:

// from an input element var filesToUpload = input.files; var file = filesToUpload[0]; var img = document.createElement("img"); var reader = new FileReader(); reader.onload = function(e) {img.src = e.target.result} reader.readAsDataURL(file); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var MAX_WIDTH = 800; var MAX_HEIGHT = 600; var width = img.width; var height = img.height; if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } canvas.width = width; canvas.height = height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); var dataurl = canvas.toDataURL("image/png"); //Post dataurl to the server with AJAX 

几年前,我解决了这个问题,并将我的解决scheme上传到github上: https://github.com/rossturner/HTML5-ImageUploader

robertc的答案使用了Mozilla Hacks博客文章中提出的解决scheme,但是我发现在调整到不是2:1(或其倍数)的缩放比例时,图像质量非常差。 我开始尝试不同的图像大小调整algorithm,虽然大多数结果是很慢,否则质量也不是很好。

最后,我想出了一个我认为可以快速执行的解决scheme,并且性能也相当不错 – 因为Mozilla解决scheme从1个canvas复制到另一个画面的速度非常快,而且不会以2:1的比例丢失图像质量,像素宽,像素高,我使用这个canvasresize的方法,直到图像在x和2 xy和2 y之间 。 在这一点上,我接着转向algorithm图像resize的最终“步骤”调整到目标的大小。 在尝试了几种不同的algorithm之后,我决定从一个不在线的博客取得双线性插值,但是可以通过互联网存档访问 ,这给出了很好的结果,下面是适用的代码:

 ImageUploader.prototype.scaleImage = function(img, completionCallback) { var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height); while (canvas.width >= (2 * this.config.maxWidth)) { canvas = this.getHalfScaleCanvas(canvas); } if (canvas.width > this.config.maxWidth) { canvas = this.scaleCanvasWithAlgorithm(canvas); } var imageData = canvas.toDataURL('image/jpeg', this.config.quality); this.performUpload(imageData, completionCallback); }; ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) { var scaledCanvas = document.createElement('canvas'); var scale = this.config.maxWidth / canvas.width; scaledCanvas.width = canvas.width * scale; scaledCanvas.height = canvas.height * scale; var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height); this.applyBilinearInterpolation(srcImgData, destImgData, scale); scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0); return scaledCanvas; }; ImageUploader.prototype.getHalfScaleCanvas = function(canvas) { var halfCanvas = document.createElement('canvas'); halfCanvas.width = canvas.width / 2; halfCanvas.height = canvas.height / 2; halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height); return halfCanvas; }; ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) { function inner(f00, f10, f01, f11, x, y) { var un_x = 1.0 - x; var un_y = 1.0 - y; return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y); } var i, j; var iyv, iy0, iy1, ixv, ix0, ix1; var idxD, idxS00, idxS10, idxS01, idxS11; var dx, dy; var r, g, b, a; for (i = 0; i < destCanvasData.height; ++i) { iyv = i / scale; iy0 = Math.floor(iyv); // Math.ceil can go over bounds iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv)); for (j = 0; j < destCanvasData.width; ++j) { ixv = j / scale; ix0 = Math.floor(ixv); // Math.ceil can go over bounds ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv)); idxD = (j + destCanvasData.width * i) * 4; // matrix to vector indices idxS00 = (ix0 + srcCanvasData.width * iy0) * 4; idxS10 = (ix1 + srcCanvasData.width * iy0) * 4; idxS01 = (ix0 + srcCanvasData.width * iy1) * 4; idxS11 = (ix1 + srcCanvasData.width * iy1) * 4; // overall coordinates to unit square dx = ixv - ix0; dy = iyv - iy0; // I let the r, g, b, a on purpose for debugging r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy); destCanvasData.data[idxD] = r; g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy); destCanvasData.data[idxD + 1] = g; b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy); destCanvasData.data[idxD + 2] = b; a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy); destCanvasData.data[idxD + 3] = a; } } }; 

这将图像缩小到config.maxWidth的宽度,保持原始高宽比。 在开发的时候,除了主要的桌面浏览器(IE9 +,Firefox,Chrome)之外,iPad / iPhone Safari还有其他的function,所以我期望它能够兼容现在HTML5的广泛应用。 请注意,canvas.toDataURL()调用需要一个MIMEtypes和图像质量,这将允许您控制质量和输出文件格式(如果您愿意,可能与input不同)。

这里没有涉及的唯一的一点就是维护方向信息,而不知道这个元数据的大小和保存图像的大小,丢失图像中的任何元数据的方向意味着在平板设备上“颠倒”的图像是尽pipe它们会在设备的相机取景器中翻转。 如果这是一个问题, 这个博客文章有一个很好的指导和代码示例如何做到这一点,我敢肯定可以集成到上面的代码。

上述更正:

 <img src="" id="image"> <input id="input" type="file" onchange="handleFiles()"> <script> function handleFiles() { var filesToUpload = document.getElementById('input').files; var file = filesToUpload[0]; // Create an image var img = document.createElement("img"); // Create a file reader var reader = new FileReader(); // Set the image once loaded into file reader reader.onload = function(e) { img.src = e.target.result; var canvas = document.createElement("canvas"); //var canvas = $("<canvas>", {"id":"testing"})[0]; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var MAX_WIDTH = 400; var MAX_HEIGHT = 300; var width = img.width; var height = img.height; if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } canvas.width = width; canvas.height = height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); var dataurl = canvas.toDataURL("image/png"); document.getElementById('image').src = dataurl; } // Load files into file reader reader.readAsDataURL(file); // Post the data /* var fd = new FormData(); fd.append("name", "some_filename.jpg"); fd.append("image", dataurl); fd.append("info", "lah_de_dah"); */ }</script> 

如果你不想重新发明轮子,你可以尝试plupload.com

Justin上面的答案修改为我工作:

  1. 增加了img.onload
  2. 用一个真实的例子展开POST请求
 function handleFiles() { var dataurl = null; var filesToUpload = document.getElementById('photo').files; var file = filesToUpload[0]; // Create an image var img = document.createElement("img"); // Create a file reader var reader = new FileReader(); // Set the image once loaded into file reader reader.onload = function(e) { img.src = e.target.result; img.onload = function () { var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var MAX_WIDTH = 800; var MAX_HEIGHT = 600; var width = img.width; var height = img.height; if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } canvas.width = width; canvas.height = height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); dataurl = canvas.toDataURL("image/jpeg"); // Post the data var fd = new FormData(); fd.append("name", "some_filename.jpg"); fd.append("image", dataurl); fd.append("info", "lah_de_dah"); $.ajax({ url: '/ajax_photo', data: fd, cache: false, contentType: false, processData: false, type: 'POST', success: function(data){ $('#form_photo')[0].reset(); location.reload(); } }); } // img.onload } // Load files into file reader reader.readAsDataURL(file); } 

被接受的答案效果很好,但resize逻辑会忽略图像大于最大值的情况(例如,height> maxHeight但width <= maxWidth)。

我认为下面的代码以更直接和更实用的方式照顾所有情况(如果使用普通的javascript,则忽略打字稿types注释):

 private scaleDownSize(width: number, height: number, maxWidth: number, maxHeight: number): {width: number, height: number} { if (width <= maxWidth && height <= maxHeight) return { width, height }; else if (width / maxWidth > height / maxHeight) return { width: maxWidth, height: height * maxWidth / width}; else return { width: width * maxHeight / height, height: maxHeight }; } 
 fd.append("image", dataurl); 

这是行不通的。 在PHP方面,你不能用这个保存文件。

改用这个代码:

 var blobBin = atob(dataurl.split(',')[1]); var array = []; for(var i = 0; i < blobBin.length; i++) { array.push(blobBin.charCodeAt(i)); } var file = new Blob([new Uint8Array(array)], {type: 'image/png', name: "avatar.png"}); fd.append("image", file); // blob file 

在canvas元素中调整图像的大小通常是个坏主意,因为它使用了最便宜的盒子插值。 由此产生的图像显着降低质量。 我build议使用可以执行Lanczos转换的http://nodeca.github.io/pica/demo/ 。 上面的演示页面显示了canvas和Lanczos方法之间的区别。

它还使用networking工作者并行调整图像大小。 还有WEBGL的实现。

有一些在线图像大小调整器使用pica来完成这项工作,例如https://myimageresizer.com