对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提升了用户体验。javascript
这种体验包括两方面:php
一、因为上传图片尺寸比较小,所以上传速度会比较快,交互会更加流畅,同时大大下降了网络异常致使上传失败风险。html
二、最重要的体验改进点:省略了图片的再加工成本。不少网站的图片上传功能都会对图片的大小进行限制,尤为是头像上传,限制5M或者2M之内是很是常见的。而后如今的数码设备拍摄功能都很是出众,一张原始图片超过2M几乎是标配,此时若是用户想把手机或相机中的某个得意图片上传做为本身的头像,就会遇到由于图片大小限制而不能上传的窘境,不得不对图片进行再处理,而这种体验其实很是很差的。若是能够在前端进行压缩,则理论上对图片尺寸的限制是没有必要的。前端
要想使用JS实现图片的压缩效果,原理其实很简单,核心API就是使用canvas
的drawImage()
方法。java
canvas
的drawImage()
方法API以下:jquery
context.drawImage(img, dx, dy);
context.drawImage(img, dx, dy, dWidth, dHeight);
context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
后面最复杂的语法虽然看上去有9大参数,但不用慌,实际上能够看出就3个参数:web
canvas
画布上规划处一片区域用来放置图片,dx, dy
为canvas元素的左上角坐标,dWidth, dHeight
指canvas元素上用在显示图片的区域大小。若是没有指定sx,sy,sWidth,sHeight
这4个参数,则图片会被拉伸或缩放在这片区域内。
canvas
画布上显示的大小和位置。sx,sy
表示图片上sx,sy
这个坐标做为左上角,而后往右下角的swidth,sheight
尺寸范围图片做为最终在canvas上显示的图片内容。
drawImage()
方法有一个很是怪异的地方,你们必定要注意,那就是5参数和9参数里面参数位置是不同的,这个和通常的API有所不一样。通常API可选参数是放在后面。可是,这里的drawImage()
9个参数时候,可选参数sx,sy,swidth,sheight
是在前面的。若是不注意这一点,有些表现会让你没法理解。ajax
下图为MDN上原理示意:canvas
对于本文的图片压缩,须要用的是是5个参数语法。举个例子,一张图片(假设图片对象是img
)的原始尺寸是4000*3000,如今须要把尺寸限制为400*300大小,很简单,原理以下代码示意:后端
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = 400; canvas.height = 300; // 核心JS就这个
context.drawImage(img,0,0,400,300);
把一张大的图片,直接画在一张小小的画布上。此时大图片就自然变成了小图片,压缩就这么实现了,是否是简单的有点超乎想象。
固然,若要落地于实际开发,咱们还须要作些其余的工做,就是要解决图片来源和图片去向的问题。
HTML5 file API可让图片在上传以前直接在浏览器中显示,一般使用FileReader
方法,代码示意以下:
var reader = new FileReader(), img = new Image(); // 读文件成功的回调
reader.onload = function(e) { // e.target.result就是图片的base64地址信息
img.src = e.target.result; }; eleFile.addEventListener('change', function (event) { reader.readAsDataURL(event.target.files[0]); });
因而,包含图片信息的context.drawImage()
方法中的img
图片就有了。
二、如何把canvas画布转换成img图像
canvas
自然提供了2个转图片的方法,一个是:
canvas.toDataURL()方法
canvas.toDataURL(mimeType, qualityArgument)
能够把图片转换成base64格式信息,纯字符的图片表示法。
其中:
mimeType
表示canvas
导出来的base64
图片的类型,默认是png格式,也便是默认值是'image/png'
,咱们也能够指定为jpg格式'image/jpeg'
或者webp等格式。
file
对象中的file.type
就是文件的mimeType类型,在转换时候正好能够直接拿来用(若是有file对象)。
qualityArgument
表示导出的图片质量,只要导出为jpg
和webp
格式的时候此参数才有效果,默认值是0.92
,是一个比较合理的图片质量输出参数,一般状况下,咱们无需再设定。
canvas.toBlob(callback, mimeType, qualityArgument)
能够把canvas转换成Blob文件,一般用在文件上传中,由于是二进制的,对后端更加友好。
和toDataURL()
方法相比,toBlob()
方法是异步的,所以多了个callback
参数,这个callback
回调方法默认的第一个参数就是转换好的blob
文件信息。
将canvas
图片转换成二进制的blob
文件,而后再ajax
上传的,代码以下:
// canvas转为blob并上传
canvas.toBlob(function (blob) { // 图片ajax上传
var xhr = new XMLHttpRequest(); // 开始上传
xhr.open("POST", 'upload.php', true); xhr.send(blob); });
因而,通过“图片→canvas压缩→图片”三步曲,咱们完成了图片前端压缩并上传的功能。
//HTML代码:
<input id="file" type="file">
//JS代码:
var eleFile = document.querySelector('#file'); // 压缩图片须要的一些元素和对象
var reader = new FileReader(), img = new Image(); // 选择的文件对象
var file = null; // 缩放图片须要的canvas
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); // base64地址图片加载完毕后
img.onload = function () { // 图片原始尺寸
var originWidth = this.width; var originHeight = this.height; // 最大尺寸限制
var maxWidth = 400, maxHeight = 400; // 目标尺寸
var targetWidth = originWidth, targetHeight = originHeight; // 图片尺寸超过400x400的限制
if (originWidth > maxWidth || originHeight > maxHeight) { if (originWidth / originHeight > maxWidth / maxHeight) { // 更宽,按照宽度限定尺寸
targetWidth = maxWidth; targetHeight = Math.round(maxWidth * (originHeight / originWidth)); } else { targetHeight = maxHeight; targetWidth = Math.round(maxHeight * (originWidth / originHeight)); } } // canvas对图片进行缩放
canvas.width = targetWidth; canvas.height = targetHeight; // 清除画布
context.clearRect(0, 0, targetWidth, targetHeight); // 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight); // canvas转为blob并上传
canvas.toBlob(function (blob) { // 图片ajax上传
var xhr = new XMLHttpRequest(); // 文件上传成功
xhr.onreadystatechange = function() { if (xhr.status == 200) { // xhr.responseText就是返回的数据
} }; // 开始上传
xhr.open("POST", 'upload.php', true); xhr.send(blob); }, file.type || 'image/png'); }; // 文件base64化,以便获知图片原始尺寸
reader.onload = function(e) { img.src = e.target.result; }; eleFile.addEventListener('change', function (event) { file = event.target.files[0]; // 选择的文件是图片
if (file.type.indexOf("image") == 0) { reader.readAsDataURL(file); } });
上传前还能够预览:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script language="javascript"> window.onload=function(){ var eleFile = document.querySelector('#jjfxSoft_iconPath'); // 压缩图片须要的一些元素和对象
var reader = new FileReader(), img = new Image(); // 选择的文件对象
var file = null; // 缩放图片须要的canvas
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); // base64地址图片加载完毕后
img.onload = function () { debugger // 图片原始尺寸
var originWidth = this.width; var originHeight = this.height; // 最大尺寸限制
var maxWidth = 300, maxHeight = 300; // 目标尺寸
var targetWidth = originWidth, targetHeight = originHeight; // 图片尺寸超过300x300的限制
if (originWidth > maxWidth || originHeight > maxHeight) { if (originWidth / originHeight > maxWidth / maxHeight) { targetWidth = maxWidth; targetHeight = Math.round(maxWidth * (originHeight / originWidth)); } else { targetHeight = maxHeight; targetWidth = Math.round(maxHeight * (originWidth / originHeight)); } } // canvas对图片进行缩放
canvas.width = targetWidth; canvas.height = targetHeight; // 清除画布
context.clearRect(0, 0, targetWidth, targetHeight); // 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight); var type = 'image/jpeg'; //将canvas元素中的图像转变为DataURL
var dataurl = canvas.toDataURL(type); $("#ceshi1").attr("src",dataurl); //抽取DataURL中的数据部分,从Base64格式转换为二进制格式
var bin = atob(dataurl.split(',')[1]); //建立空的Uint8Array
var buffer = new Uint8Array(bin.length); //将图像数据逐字节放入Uint8Array中
for (var i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } //利用Uint8Array建立Blob对象
var blob = new Blob([buffer.buffer], {type: type}); var url = window.URL.createObjectURL(blob); $("#ceshi").attr("src",url); }; // 文件base64化,以便获知图片原始尺寸
reader.onload = function(e) { img.src = e.target.result; }; eleFile.addEventListener('change', function (event) { file = event.target.files[0]; // 选择的文件是图片
if (file.type.indexOf("image") == 0) { reader.readAsDataURL(file); } }); } </script>
</head>
<body>
<img id="ceshi">
<img id="ceshi1">
<input name="file" type="file" id="jjfxSoft_iconPath">
</body>
</html>