以移动端为例,在H5上传图片时,由于机型 / 网速 / 流量等限制,在低版本机型上经常会出现上传的图片太大导致上传很慢甚至崩溃的情况,所以需要对在某些情况下需要对上传的图片进行压缩上传。
以下以移动端使用Vue框架为例:
(以下主要实现思路来自张鑫旭大神的博客:https://www.zhangxinxu.com/wordpress/2017/07/html5-canvas-image-compress-upload/)
话不多说进入正题
上面这句代码中可以发现,我没有指定上传的文件的格式,因为在之前的开发过程中发现,input在设置了accpet=“image/*”之后,在某些低版本安卓机(微信浏览器)上,会出现上传jpeg格式的图片无法触发input的change的事件的情况,所以后来就把input的图片类型去掉了,验证文件类型在change事件触发后处理或者交由后端校验。
在接收到需要上传的图片之后,我们可以做一些限制操作,比如限制上传图片的大小 / 校验文件的格式等等,这里就不写出来了。直接切入正题:
changeImg(e){
const imgFile = e.target.files[0];
const zipImgPromise = (imgFile) => {
return new Promise((resolve, reject) => {
let reader = new FileReader(); // 定义一个fileReader
reader.readAsDataURL(imgFile); // 将图片转换成base64后可以得到图片的宽高
let img = new Image();
reader.onload = () => {
img.src = reader.result;
}
img.onload = () => {
// 图片原始尺寸
let originWidth = img.width;
let originHeight = img.height;
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
// 最大尺寸限制
let maxWidth = 800, maxHeight = 800;
// 目标尺寸
let 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转换成dataUrl
let dataUrl = canvas.toDataURL('image/png');
// 转换成formdata格式用于上传图片
let blob = this.dataURItoBlob(dataUrl);
if (blob) {
resolve(blob);
} else {
reject(new Error('Error!!'));
}
}
});
};
zipImgPromise(imgFile).then(blob => {
let file = new FormData(); // 创建form对象
file.append('file', blob, imgFile.name); // 通过append向form对象添加数据
file.append('chunk', '0'); // 添加form表单中其他数据
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
// 开始上传图片
// ...
// 这里用于执行上传图片的XHR操作
// ...
$http.post(‘api’, file, config).then(res => {
// ...
}).catch(error => {
// ...
});
}).catch(error => {
// 处理zipImgPromise和前一个回调函数运行时发生的错误
console.log('发生错误', error);
});Ï
},
dataURItoBlob(dataURI) {
let byteString = atob(dataURI.split(',')[1]);
let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
let ab = new ArrayBuffer(byteString.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
let bb = new Blob([ab], { "type": mimeString });
return bb;
}
其上传主要思路其实就是按比例缩放图片,主要运用了canvas
的drawImage()
方法。
canvas
的drawImage()
方法API如下:
context.drawImage(img, dx, dy);
context.drawImage(img, dx, dy, dWidth, dHeight);
context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
dx, dy, dWidth, dHeight——表示在canvas
画布上规划处一片区域用来放置图片,dx, dy
为canvas元素的左上角坐标,dWidth, dHeight
指canvas元素上用在显示图片的区域大小。如果没有指定sx,sy,sWidth,sHeight
这4个参数,则图片会被拉伸或缩放在这片区域内。
所以我们这里简单来说是通过的将一张原始尺寸是2000*1300的图片,通过刚刚说的canvas的drawImage()方法,
把尺寸限制为400*300大小,很简单,主要代码就是下面这4句
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 300;
// 核心JS
context.drawImage(img,0,0,400,300);
这里通俗来说就是把一张大的图片,直接画在一张小的画布上,这样大图片就变成了小图片,压缩图片就是这样实现的。(听起来有点amazing~)
所以经过了图片→canvas压缩→图片的这三个步骤,我们的压缩图片就完成啦~~