利用谷歌调式工具发现,图片大小直接影响着首屏加载时间。
且考虑到后期服务端压力,图片压缩特别必要。
本文是前端利用canvas实现图片。参考文章:https://www.cnblogs.com/007sx/p/7583202.html
本文将其改为插件形式,适合单文件压缩,多文件可以采用生成多个二进制文件的方法,然后一并上传。具体后面研究。
说说原理,压缩涉及三个关键点:
1,一个图片前端可被加载,基于file:协议的路径是不能产生onload事件,所以需要借助浏览器的接口将图片转为可加载文件,一种是通过FileReader,另一种是
通过URL.createObjectURL。
2,利用canvas,获取图片的高度和宽度之后,利用drawImage输出图片,再利用canvas的toDataURL输出base64的图片。
3,将base64转为二进制文件。
案例代码:
1 <!DOCTYPE html> 2 <html> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>XMLHttpRequest上传文件压缩上传图片文件</title> 7 </head> 8 9 <body> 10 <input type="file" id="file" name="myfile" accept="image/x-png, image/jpg, image/jpeg, image/gif" /> 11 <input type="button" onclick="UpladFile()" value="上传" /> 12 <script type="text/javascript"> 13 (function() { 14 function compress(opts, fileObj) { 15 this.defaults = { 16 id: "#file", 17 quality: 0.4, 18 url: "", 19 callback: function(bl) { 20 console.log(bl); 21 } 22 }; 23 24 if (typeof opts === "object") { 25 this.options = Object.assign({}, this.defaults, opts) 26 } else { 27 this.options = this.defaults; 28 } 29 fileObj = fileObj || document.querySelector(this.options.id).files[0]; 30 this.init(fileObj); 31 } 32 compress.prototype = { 33 version: "1.0.1", 34 init: function(fileObj) { 35 _this = this; 36 //promise处理异步 37 this.photoCompress(fileObj).then(function(res) { 38 return _this.canvasDataUrl.call(_this, res); 39 }).catch(function(err) { 40 console.log(err); 41 }).then(function(res) { 42 var bl = _this.convertBase64UrlToBlob(res); 43 _this.options.callback(bl); 44 }).catch(function(err) { 45 console.log(err); 46 }) 47 48 }, 49 photoCompress: function(file) { 50 var ready = new FileReader(); 51 ready.readAsDataURL(file); 52 return new Promise(function(resolve, reject) { 53 ready.onload = function() { 54 var re = this.result; 55 resolve(re); 56 } 57 ready.onerror = function(err) { 58 reject(err) 59 } 60 }) 61 62 }, 63 canvasDataUrl: function(res) { 64 65 var img = new Image(); 66 img.src = res; 67 _this = this; 68 return new Promise(function(resolve, reject) { 69 img.onload = function() { 70 // 默认按比例压缩 71 var w = this.width, 72 h = this.height; 73 //生成canvas 74 var canvas = document.createElement('canvas'); 75 var ctx = canvas.getContext('2d'); 76 // 创建属性节点 77 var anw = document.createAttribute("width"); 78 anw.nodeValue = w; 79 var anh = document.createAttribute("height"); 80 anh.nodeValue = h; 81 canvas.setAttributeNode(anw); 82 canvas.setAttributeNode(anh); 83 ctx.drawImage(this, 0, 0, w, h); 84 85 // quality值越小,所绘制出的图像越模糊 86 var base64 = canvas.toDataURL('image/jpeg', _this.options.quality); 87 // 回调函数返回base64的值 88 resolve(base64) 89 } 90 img.onerror = function(err) { 91 reject(err) 92 } 93 }) 94 }, 95 96 //base64转为二进制数据,后端可直接利用 97 convertBase64UrlToBlob: function(urlData) { 98 var arr = urlData.split(','), 99 mime = arr[0].match(/:(.*?);/)[1], 100 bstr = atob(arr[1]), 101 n = bstr.length, 102 u8arr = new Uint8Array(n); 103 while (n--) { 104 u8arr[n] = bstr.charCodeAt(n); 105 } 106 return new Blob([u8arr], { type: mime }); 107 } 108 } 109 window.compress = compress; 110 111 })(); 112 113 //上传文件方法 114 function UpladFile() { 115 116 var url = "./upload"; // 接收上传文件的后台地址 117 new compress({ 118 quality: 0.4, 119 url: url, 120 callback: function(bl) { 121 var form = new FormData(); 122 form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象 123 var xhr = new XMLHttpRequest(); 124 xhr.open("post", url, true); 125 xhr.onreadystatechange = function(evt) { 126 if (xhr.readyState === 4 && xhr.status === 200) { 127 uploadComplete(evt); 128 } 129 } 130 131 xhr.send(form); //开始上传,发送form数据 132 } 133 }) 134 } 135 136 //上传成功响应 137 function uploadComplete(evt) { 138 //服务断接收完文件返回的结果 139 var data = JSON.parse(evt.target.responseText); 140 if (data.success) { 141 alert("上传成功!"); 142 } else { 143 alert("上传失败!"); 144 } 145 146 } 147 </script> 148 </body> 149 150 </html>
后端采用thinkphp5,代码index控制器下,方法
1 public function upload(){ 2 3 // 获取表单上传文件 例如上传了001.jpg 4 $file = request()->file('file'); 5 $arr=array("code"=>1,"success"=>true); 6 // 移动到框架应用根目录/public/uploads/ 目录下 7 if($file){ 8 $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads'); 9 if($info){ 10 11 echo json_encode($arr); 12 13 }else{ 14 15 return "error"; 16 // 上传失败获取错误信息 17 echo $file->getError(); 18 } 19 }; 20 }
多张图片上传前压缩
1 <!DOCTYPE html> 2 <html> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>XMLHttpRequest上传文件压缩上传图片文件多</title> 7 </head> 8 9 <body> 10 <input type="file" id="files" name="myfile" multiple accept="image/x-png, image/jpg, image/jpeg, image/gif" /> 11 <input type="button" onclick="UpladFile()" value="上传" /> 12 <script type="text/javascript"> 13 /* 14 三个参数 15 file:一个是文件(类型是图片格式), 16 w:一个是文件压缩的后宽度,宽度越小,字节越小 17 objDiv:一个是容器或者回调函数 18 photoCompress() 19 */ 20 function photoCompress(file, w, objDiv) { 21 var ready = new FileReader(); 22 /*开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/ 23 ready.readAsDataURL(file); 24 ready.onload = function() { 25 var re = this.result; 26 canvasDataURL(re, w, objDiv) 27 } 28 } 29 30 function canvasDataURL(path, obj, callback) { 31 var img = new Image(); 32 img.src = path; 33 img.onload = function() { 34 var that = this; 35 // 默认按比例压缩 36 var w = that.width, 37 h = that.height, 38 scale = w / h; 39 w = obj.width || w; 40 h = obj.height || (w / scale); 41 var quality = 0.7; // 默认图片质量为0.7 42 //生成canvas 43 var canvas = document.createElement('canvas'); 44 var ctx = canvas.getContext('2d'); 45 // 创建属性节点 46 var anw = document.createAttribute("width"); 47 anw.nodeValue = w; 48 var anh = document.createAttribute("height"); 49 anh.nodeValue = h; 50 canvas.setAttributeNode(anw); 51 canvas.setAttributeNode(anh); 52 ctx.drawImage(that, 0, 0, w, h); 53 // 图像质量 54 if (obj.quality && obj.quality <= 1 && obj.quality > 0) { 55 quality = obj.quality; 56 } 57 // quality值越小,所绘制出的图像越模糊 58 var base64 = canvas.toDataURL('image/jpeg', quality); 59 // 回调函数返回base64的值 60 callback(base64); 61 } 62 } 63 /** 64 * 将以base64的图片url数据转换为Blob 65 * @param urlData 66 * 用url方式表示的base64图片数据 67 */ 68 function convertBase64UrlToBlob(urlData) { 69 var arr = urlData.split(','), 70 mime = arr[0].match(/:(.*?);/)[1], 71 bstr = atob(arr[1]), 72 n = bstr.length, 73 u8arr = new Uint8Array(n); 74 while (n--) { 75 u8arr[n] = bstr.charCodeAt(n); 76 } 77 return new Blob([u8arr], { type: mime }); 78 } 79 80 //上传文件方法 81 function UpladFile() { 82 83 var url = "./upload"; // 接收上传文件的后台地址 84 var form = new FormData(); 85 var files = document.querySelector("#files").files; 86 files = Array.prototype.slice.call(files); 87 files.forEach(function(val, index) { 88 photoCompress(val, { 89 quality: 0.2 90 }, function(base64Codes) { 91 console.log(index); 92 //console.log("压缩后:" + base.length / 1024 + " " + base); 93 var bl = convertBase64UrlToBlob(base64Codes); 94 form.append("file" + index, bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象 95 if (index + 1 === files.length) { 96 xhr = new XMLHttpRequest(); // XMLHttpRequest 对象 97 xhr.open("post", url, true); //post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。 98 xhr.onload = uploadComplete; //请求完成 99 xhr.send(form); //开始上传,发送form数据 100 } 101 }); 102 }) 103 } 104 105 //上传成功响应 106 function uploadComplete(evt) { 107 //服务断接收完文件返回的结果 108 var data = JSON.parse(evt.target.responseText); 109 if (data.success) { 110 alert("上传成功!"); 111 } else { 112 alert("上传失败!"); 113 } 114 115 } 116 </script> 117 </body> 118 119 </html>