简单记录下今早做H5上传中一些代码还有坑
一、展示
因为前端上传文件是必须通过form表单的,不能使用ajax,这样的话一个移动页面放入一个type为file的input真心不怎么好看,如下图,很挫有没有
解决办法找了下,PC上有些是把这个input换成flash,采用jquery的工具库比如uploadify来做,但是移动端大部分浏览器是不支持flash的。所以最后采用的办法还是用form表单的形式,只是把这个form和input的透明度设置为0,让它们和准备显示的内容同时在一个div中,显示的内容可以做成自己想要的样子。代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"> <title></title> <style> div{width: 100%;} .logo img{display:block; margin:0 auto;} .upload{position: relative;width: 80px;height: 18px;line-height: 18px;background: #2fc7c9;text-align: center; color: #FFF;padding: 0px 5px;-webkit-border-radius: 2px;border-radius: 2px; margin: 0 auto; } .upload form{width:100%;position:absolute; left:0; top:0;opacity:0; filter:alpha(opacity=0);} .upload form input{width: 100%;} </style> </head> <body> <div class="logo"> <img src="img/1.jpg" /> </div> <div class="upload"> <p>上传图片</p> <form> <input type="file" /> </form> </div> </body> </html>
样子如左图,这样展现就在“上传图片”这个p标签中,点击它就有选择file的效果
二、JS代码
我这边写的蛮简单的,只是用了下h5上传的的基本功能
html代码如下,action为要请求的路径,我这边做的是当文件发生改变时就上传修改头像,input标签的name属性不能省去,具体跟后端接口有关
<form id="uploadForm" enctype="multipart/form-data" method="post" action="XXXXXX"> <input type="file" name="imageFile" id="imageFile" onchange="fileSelected()" /> </form>
var iMaxFilesize = 2097152; //2M window.fileSelected = function() { var oFile = document.getElementById(\'imageFile\').files[0]; //读取文件 var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; if (!rFilter.test(oFile.type)) { alert("文件格式必须为图片"); return; } if (oFile.size > iMaxFilesize) { alert("图片大小不能超过2M"); return; } var vFD = new FormData(document.getElementById(\'uploadForm\')), //建立请求和数据 oXHR = new XMLHttpRequest(); oXHR.addEventListener(\'load\', function(resUpload) { //成功 }, false); oXHR.addEventListener(\'error\', function() { //失败 }, false); oXHR.addEventListener(\'abort\', function() { //上传中断 }, false); oXHR.open(\'POST\', actionUrl); oXHR.send(vFD); };
三、图片压缩
在开发中,特别是在移动端往往一张图片大小在3,4M左右,这样的图片上传会给服务器带来不少压力,同时也有不少接口对img的大小有所要求,比如不能超过200K,那手机相册的照片大多是见了鬼的,都上传不了。在html5的功能中,可以将图片压缩大小(尺寸)放到canvas画布上,然后截取canvas画布上的图片,转变为二进制的数据,通过blob进行再次压缩生成图片文件,这样手机上4M左右图片传到服务器上也就100K左右了。
<input type="file" name="imageFile" id="imageFile" onchange="fileSelected()" /> <form id="uploadForm" enctype="multipart/form-data" method="post" action="XXXXXX"> <input hidden="hidden" name="param" value="test" /> </form>
当文件改变时,对图片进行压缩上传
window.fileSelected = function() { var _this = $(this); var file = this.files[0]; var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; if(!rFilter.test(file.type)) { alert("文件格式必须为图片"); return; } /*开始进行网络加载*/ _this.css("display", "none"); //目的是为了屏蔽点击事件 var reader = new FileReader() , image = new Image() , canvas = document.createElement("canvas") , ctx = canvas.getContext("2d"); reader.onload = function() { //文件加载完成 var url = reader.result; image.src = url; }; image.onload = function() { //图片加载完成 var w = image.naturalWidth , h = image.naturalHeight , scale = 3; //图片缩放比例,这里是把图片大小高宽均缩小3倍 canvas.width = w / scale; canvas.height = h / scale; ctx.drawImage(image, 0 , 0 , w , h , 0 , 0 , canvas.width , canvas.height); fileUpload(); }; reader.readAsDataURL(file); //用文件加载器加载文件 function fileUpload() { //文件上传方法 var quality = 0.3; //图片的质量,这里设置的是0.3 var data = canvas.toDataURL("image/jpeg", quality);//获取画布图片,并且要jpg格式 data = data.split(\',\')[1]; data = window.atob(data); var ia = new Uint8Array(data.length); for(var i = 0; i < data.length; i++) { ia[i] = data.charCodeAt(i); } var blob = new Blob([ia], { //以上均为二进制参数处理,从而获取一个blob对象 type: "image/jpeg" }); var fd = new FormData(document.getElementById("uploadForm")); fd.append("XXX" , blob , "upload.jpg"); //向form中加入图片数据,name属性是XXX,文件名是upload.jpg var xhr = new XMLHttpRequest(); xhr.addEventListener(\'load\', function(resUpload) { _this.css("display", ""); //请求成功 }, false); xhr.addEventListener(\'error\', function(){ _this.css("display", ""); //请求失败 }, false); xhr.addEventListener(\'abort\', function(){ _this.css("display", ""); //上传终止 }, false); xhr.open(\'POST\', "http://XXXXXXXXXXXXX");//请求地址 xhr.send(fd);//发送 } };
关键代码
reader.readAsDataURL(file);
将文件读取为DataURL
canvas.toDataURL(type, encoderOptions);
实际上就是读取canvas画布上图片的数据。其默认是png格式,如果第一个参数type是image/jpeg的话,第二个参数encoderOptions就可以用来设置图片的压缩质量。
var quality = 0.3; //图片的质量,这里设置的是0.3 var data = canvas.toDataURL("image/jpeg", quality);//获取画布图片,并且要jpg格式 data = data.split(\',\')[1]; data = window.atob(data); var ia = new Uint8Array(data.length); for(var i = 0; i < data.length; i++) { ia[i] = data.charCodeAt(i); } var blob = new Blob([ia], { //以上均为二进制参数处理,从而获取一个blob对象 type: "image/jpeg" });
这一段代码的的目的是为了解码图片数据,然后返回一个Blob对象,对象的格式是jpg。aton其作用是做解码,因为图片格式的base64,该方法解码出来可能是一堆乱码,Uint8Array返回的是8进制整型数组。
Blob是存储二进制文件的容器,典型的Blob对象是一个图片或者声音文件,其默认是PNG格式。最后的blob就可以传给服务端了。
整理成一个插件,代码如下
(function(window,undefind){ function imgUpLoad(options){ var defaults = { inputId : "" , //输入框ID formId : "" , //表单ID paramName : "" , //输入框的name requestUrl : "" , //请求的URL imgSizeScale : 3 , //图片缩放比例 imgQuality : 0.3 , //图片质量 sucFun : undefind , //成功的回调函数 errFun : undefind , //失败的回调函数 abortFun : undefind //上传取消的回调函数 }; var opts = $.extend(true , defaults , options || {}) , _this = document.getElementById(opts.inputId); _this.addEventListener(\'change\' , fileChange , false); function fileChange() { var file = _this.files[0]; var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; if(!rFilter.test(file.type)) { alert("文件格式必须为图片"); return; } var reader = new FileReader() , image = new Image() , canvas = document.createElement("canvas") , ctx = canvas.getContext("2d"); startFileLoad(reader , image , canvas , ctx); } function startFileLoad(reader , image , canvas , ctx){ //文件加载 reader.onload = function() { //文件加载完成 var url = reader.result; image.src = url; }; image.onload = function() { //图片加载完成 var w = image.naturalWidth , h = image.naturalHeight; canvas.width = w / opts.imgSizeScale; canvas.height = h / opts.imgSizeScale; ctx.drawImage(image, 0 , 0 , w , h , 0 , 0 , canvas.width , canvas.height); fileUpload(canvas); }; reader.readAsDataURL(file); } function fileUpload(canvas){ //文件上传 var blob = getBlob(canvas); var fd = new FormData(document.getElementById(opts.formId)); fd.append(opts.paramName , blob , "upload.jpg"); var xhr = new XMLHttpRequest(); xhr.addEventListener(\'load\', function(resUpload) { //请求成功 _this.style.display = ""; if(opts.sucFun && typeof opts.sucFun === "function") opts.sucFun(resUpload.currentTarget.response); }, false); xhr.addEventListener(\'error\', function(){ //请求失败 _this.style.display = ""; if(opts.errFun && typeof opts.errFun === "function") opts.errFun(); }, false); xhr.addEventListener(\'abort\', function(){ //上传终止 _this.style.display = ""; if(opts.abortFun && typeof opts.abortFun === "function") opts.abortFun(); }, false); xhr.open(\'POST\', opts.requestUrl);//请求地址 xhr.send(fd);//发送 } function getBlob(canvas){ //获取blob对象 var data = canvas.toDataURL("image/jpeg", opts.imgQuality); data = data.split(\',\')[1]; data = window.atob(data); var ia = new Uint8Array(data.length); for(var i = 0; i < data.length; i++) { ia[i] = data.charCodeAt(i); } return new Blob([ia], { type: "image/jpeg" }); } } window.imgUpLoad = imgUpLoad; })(window);