随着手机的拍照效果的不断提升,必然使得单张图片的存储变大,几M到十几M不等,因此大图片上传不仅占用服务器资源、耗流量,也影响了用户体验,因而在前端方面有必要用js进行图片压缩。

一、 HTML5 canvas drawImage() 方法

canvas的drawImage()方法是前端图片压缩的核心方法,其有三种用法:

1
2
3
//获取canvas对象和图片对象
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

** 1.在画布上定位图像 **

context.drawImage(img,x,y);
参数:img规定要使用的图像、画布或视频;x在画布放置图像的x坐标位置;y在画布放置图像的y坐标位置。

** 2.在画布上定位图像,并规定图像的宽度和高度 **

context.drawImage(img,x,y,width,height);
参数:width可选。要使用的图像的宽度。(伸展或缩小图像);height可选。要使用的图像的高度。(伸展或缩小图像)。

** 3.剪切图像,并在画布上定位被剪切的部分 **

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
参数:sx可选。开始剪切的x坐标位置;sy可选。开始剪切的y坐标位置;swidth可选。被剪切图像的宽度;sheight可选。被剪切图像的高度。

二、 HTML5 file API 显示图片

HTML5的file API可以在图片上传之前直接在浏览器中显示,通常使用FileReader()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//图片上传元素
var eleFile = document.querySelector("#imgfile");
// 选择的文件对象
var file = null;
// 压缩图片需要的一些元素和对象
var reader = new FileReader(), img = new Image();
// 文件base64化,以便获知图片原始尺寸
reader.onload = function (e) {
img.src = e.target.result; //图片base64地址
};
eleFile.addEventListener('change', function (event) {
file = event.target.files[0];
// 选择的文件是图片
if (file.type.indexOf("image") == 0) {
reader.readAsDataURL(file); //base64读取
}
});

三、 如何把canvas画布转成img图像

** 1. HTMLCanvasElement.toDataURL() **

该方法可以把图片转换成base64格式信息,纯字符的图片表示法。

canvas.toDataURL(type, encoderOptions);
参数:type可选。图片格式,默认为 image/png;encoderOptions可选。在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

1
2
3
4
5
6
7
8
var canvas = document.createElement('canvas');
canvas.setAttribute("id","canvas");
canvas.setAttribute("width","5");
canvas.setAttribute("height","5");
var dataURL = canvas.toDataURL();
console.log(dataURL);
// "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby
// blAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"

** 2. HTMLCanvasElement.toBlob() **

该方法可以把canvas转换成Blob文件,通常用在文件上传中,因为是二进制的,对后端更加友好。

canvas.toBlob(callback, type, encoderOptions);
参数:type可选。图片格式,默认为 image/png;encoderOptions可选。在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

toDataURL()方法相比,toBlob()方法是异步的,因此多了个callback参数,这个callback回调方法默认的第一个参数就是转换好的blob文件信息,本文demo的文件上传就是将canvas图片转换成二进制的blob文件,然后再ajax上传的,代码如下:

1
2
3
4
5
6
7
8
// canvas转为blob并上传
canvas.toBlob(function (blob) {
// 图片ajax上传
var xhr = new XMLHttpRequest();
// 开始上传
xhr.open("POST", 'upload.php', true);
xhr.send(blob);
});

于是,经过“图片→canvas压缩→图片”三步曲,我们完成了图片前端压缩并上传的功能。

以下是完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//压缩上传
var eleFile = document.querySelector("#imgfile");

// 压缩图片需要的一些元素和对象
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;
console.log(targetWidth + "|" + targetHeight);
// 清除画布
context.clearRect(0, 0, targetWidth, targetHeight);
// 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight);
//上传的formData格式
var formData = new FormData();
//追加数据
formData.append("sessionid", $("#user_head_form #sessionid").val());
formData.append("host", $("#user_head_form input[name=host]").val());
formData.append("fileName", $("#user_head_form input[name=fileName]").val());
// canvas转为blob并上传
canvas.toBlob(function (blob) {
$('#user_head_upload_box').hide();
$('#user_head_show_box').show();
//formData放入blob
formData.append("head", blob);

console.log(blob);
// 图片ajax上传
$.ajax({
url: $("#user_head_form").attr("action"),
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false,
dataType: "json",
beforeSend: function () {
console.log("before");
},
success: function (data) {
if (data.code == 1) {
console.log(data.data);
$("#user_head_origin").attr("src", data.data);
} else {
console.log("Other");
}
}
});
});
};

// 文件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);

}
});

//压缩上传结束

*注:参考文章MDN HTMLCanvasElement.toBlob()MDN HTMLCanvasElement.toDataURL()HTML5 file API加canvas实现图片前端JS压缩并上传