利用谷歌调式工具发现,图片大小直接影响着首屏加载时间。
且考虑到后期服务端压力,图片压缩特别必要。
本文是前端利用canvas实现图片。参考文章:https://www.cnblogs.com/007sx/p/7583202.html
本文将其改为插件形式,适合单文件压缩,多文件可以采用生成多个二进制文件的方法,然后一并上传。具体后面研究。
说说原理,压缩涉及三个关键点:
1,一个图片前端可被加载,基于file:协议的路径是不能产生onload事件,所以需要借助浏览器的接口将图片转为可加载文件,一种是通过FileReader,另一种是
通过URL.createObjectURL。
2,利用canvas,获取图片的高度和宽度之后,利用drawImage输出图片,再利用canvas的toDataURL输出base64的图片。
3,将base64转为二进制文件。
案例代码:
<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>XMLHttpRequest上传文件压缩上传图片文件</title>
</head> <body>
<input type="file" id="file" name="myfile" accept="image/x-png, image/jpg, image/jpeg, image/gif" />
<input type="button" onclick="UpladFile()" value="上传" />
<script type="text/javascript">
(function() {
function compress(opts, fileObj) {
this.defaults = {
id: "#file",
quality: 0.4,
url: "",
callback: function(bl) {
console.log(bl);
}
}; if (typeof opts === "object") {
this.options = Object.assign({}, this.defaults, opts)
} else {
this.options = this.defaults;
}
fileObj = fileObj || document.querySelector(this.options.id).files[];
this.init(fileObj);
}
compress.prototype = {
version: "1.0.1",
init: function(fileObj) {
_this = this;
//promise处理异步
this.photoCompress(fileObj).then(function(res) {
return _this.canvasDataUrl.call(_this, res);
}).catch(function(err) {
console.log(err);
}).then(function(res) {
var bl = _this.convertBase64UrlToBlob(res);
_this.options.callback(bl);
}).catch(function(err) {
console.log(err);
}) },
photoCompress: function(file) {
var ready = new FileReader();
ready.readAsDataURL(file);
return new Promise(function(resolve, reject) {
ready.onload = function() {
var re = this.result;
resolve(re);
}
ready.onerror = function(err) {
reject(err)
}
}) },
canvasDataUrl: function(res) { var img = new Image();
img.src = res;
_this = this;
return new Promise(function(resolve, reject) {
img.onload = function() {
// 默认按比例压缩
var w = this.width,
h = this.height;
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(this, , , w, h); // quality值越小,所绘制出的图像越模糊
var base64 = canvas.toDataURL('image/jpeg', _this.options.quality);
// 回调函数返回base64的值
resolve(base64)
}
img.onerror = function(err) {
reject(err)
}
})
}, //base64转为二进制数据,后端可直接利用
convertBase64UrlToBlob: function(urlData) {
var arr = urlData.split(','),
mime = arr[].match(/:(.*?);/)[],
bstr = atob(arr[]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
}
window.compress = compress; })(); //上传文件方法
function UpladFile() { var url = "./upload"; // 接收上传文件的后台地址
new compress({
quality: 0.4,
url: url,
callback: function(bl) {
var form = new FormData();
form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象
var xhr = new XMLHttpRequest();
xhr.open("post", url, true);
xhr.onreadystatechange = function(evt) {
if (xhr.readyState === && xhr.status === ) {
uploadComplete(evt);
}
} xhr.send(form); //开始上传,发送form数据
}
})
} //上传成功响应
function uploadComplete(evt) {
//服务断接收完文件返回的结果
var data = JSON.parse(evt.target.responseText);
if (data.success) {
alert("上传成功!");
} else {
alert("上传失败!");
} }
</script>
</body> </html>
后端采用thinkphp5,代码index控制器下,方法
public function upload(){ // 获取表单上传文件 例如上传了001.jpg
$file = request()->file('file');
$arr=array("code"=>,"success"=>true);
// 移动到框架应用根目录/public/uploads/ 目录下
if($file){
$info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
if($info){ echo json_encode($arr); }else{ return "error";
// 上传失败获取错误信息
echo $file->getError();
}
};
}
多张图片上传前压缩
<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>XMLHttpRequest上传文件压缩上传图片文件多</title>
</head> <body>
<input type="file" id="files" name="myfile" multiple accept="image/x-png, image/jpg, image/jpeg, image/gif" />
<input type="button" onclick="UpladFile()" value="上传" />
<script type="text/javascript">
/*
三个参数
file:一个是文件(类型是图片格式),
w:一个是文件压缩的后宽度,宽度越小,字节越小
objDiv:一个是容器或者回调函数
photoCompress()
*/
function photoCompress(file, w, objDiv) {
var ready = new FileReader();
/*开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/
ready.readAsDataURL(file);
ready.onload = function() {
var re = this.result;
canvasDataURL(re, w, objDiv)
}
} function canvasDataURL(path, obj, callback) {
var img = new Image();
img.src = path;
img.onload = function() {
var that = this;
// 默认按比例压缩
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || (w / scale);
var quality = 0.7; // 默认图片质量为0.7
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, , , w, h);
// 图像质量
if (obj.quality && obj.quality <= && obj.quality > ) {
quality = obj.quality;
}
// quality值越小,所绘制出的图像越模糊
var base64 = canvas.toDataURL('image/jpeg', quality);
// 回调函数返回base64的值
callback(base64);
}
}
/**
* 将以base64的图片url数据转换为Blob
* @param urlData
* 用url方式表示的base64图片数据
*/
function convertBase64UrlToBlob(urlData) {
var arr = urlData.split(','),
mime = arr[].match(/:(.*?);/)[],
bstr = atob(arr[]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
} //上传文件方法
function UpladFile() { var url = "./upload"; // 接收上传文件的后台地址
var form = new FormData();
var files = document.querySelector("#files").files;
files = Array.prototype.slice.call(files);
files.forEach(function(val, index) {
photoCompress(val, {
quality: 0.2
}, function(base64Codes) {
console.log(index);
//console.log("压缩后:" + base.length / 1024 + " " + base);
var bl = convertBase64UrlToBlob(base64Codes);
form.append("file" + index, bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象
if (index + === files.length) {
xhr = new XMLHttpRequest(); // XMLHttpRequest 对象
xhr.open("post", url, true); //post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。
xhr.onload = uploadComplete; //请求完成
xhr.send(form); //开始上传,发送form数据
}
});
})
} //上传成功响应
function uploadComplete(evt) {
//服务断接收完文件返回的结果
var data = JSON.parse(evt.target.responseText);
if (data.success) {
alert("上传成功!");
} else {
alert("上传失败!");
} }
</script>
</body> </html>