对于大部分系统和软件来说,上传图片是必不可少的功能,各位老哥们是否还在用原生标签<input type="file">
去直接实现呢?
这种粗糙的方式很符合我们后端开发者们的口味,毕竟,功能实现才是核心,其他交给我们的前端和美工小姐姐就好。但俗话说的好:人靠衣装,佛靠金装。在这个“颜值”当道的时代,给用户丝滑的体验,让甲方爸爸拍手叫好,给产品经理有吹水的资本还是很重要的。
好了废话不多说,先看效果:
作为一名合格的程序员,具有“拿来主义”的思想很重要,毕竟谁能拒绝无脑ctrl+cv呢?下面的代码实现是基于imgFileupload.js
进行优化,并修复了一些bug,能更方便获取文件,和后端互动的更加自然。
CSS:imgfileupload.css
*{ margin:0;padding:0; } html,body{ font-family:Arial,Helvetica,sans-serif; } li{ list-style: none; } img{ border:none;display: block } .imgFileUploade{ width: 1024px;margin: 50px auto; } .imgFileUploade{ width: 100%;padding: 10px; margin-top: -4px; } .imgFileUploade .header{ height: 50px;width: 100%;line-height:50px; } .imgFileUploade .header span{ display: block;float:left; } .imgFileUploade .header span.imgTitle{ line-height:50px; } .imgFileUploade .header span.imgTitle b{ color:red;margin:0 5px;line-height: 57px;display: block;float: right;font-size: 20px; } .imgFileUploade .header span.imgClick{ width: 50px;height: 50px;margin-left: 10px;cursor: pointer; background: url(/img/addUpload.png) no-repeat center center;background-size:cover; } .imgFileUploade .header span.imgcontent{ color:#999;line-height: 50px; } .imgFileUploade .imgAll{ width: 100%; margin-top: 5px; } .imgFileUploade .imgAll ul:after{ visibility: hidden; display: block; font-size: 0; content: "."; clear: both; height: 0 } .imgFileUploade .imgAll li{ width: 100px;height: 100px;border:solid 1px #ccc;margin:8px 5px;float: left;position: relative;box-shadow: 0 0 10px #eee; } .imgFileUploade .imgAll li img{ position: absolute;top:0;left:0;width: 100%;height: 100%;display: block; } .delImg{ position: absolute;top:-10px;right:-7px;width: 22px;height: 22px;background: #000;border-radius: 50%;display: block;text-align: center;line-height: 22px;color:#fff;font-weight: 700;font-style:normal;cursor: pointer; } /*上传图片的div容器*/ .imgupBox,.imgupBox1,.imgupBox2,.imgupBox3{ border:solid 1px #ccc; }
JS:imgFileupload.js
~(function(win){ var htmls = '<input type="file" class="imgFiles" style="display: none" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" multiple>'+ '<div class="header">'+ ' <span class="imgClick">'+ ' </span>'+ ' <span class="imgcontent">'+ ' 最多上传'+ ' </span>'+ '</div>'+ '<div class="imgAll">'+ ' <ul>'+ ' </ul>'+ '</div>'; var ImgUploadeFiles = function(obj,fn){ var _this = this; this.bom = document.querySelector(obj); if(fn) fn.call(_this); this.ready(); }; ImgUploadeFiles.prototype = { init : function(o){ this.MAX = o.MAX || 5; this.callback = o.callback; this.MW = o.MW || 10000; this.MH = o.MH || 10000; }, ready : function(){ var _self = this; this.dom = document.createElement('div'); this.dom.className = 'imgFileUploade'; this.dom.innerHTML = htmls; this.bom.appendChild(this.dom); this.files = this.bom.querySelector('.imgFiles'); this.fileClick = this.bom.querySelector('.imgClick'); this.fileBtn(this.fileClick,this.files); this.imgcontent = this.bom.querySelector('.imgcontent'); this.imgcontent.innerHTML = '最多上传<b style="color:red">'+this.MAX+'</b>张'+_self.MW+' * '+_self.MH+'像素的图片'; }, fileBtn : function(c,f){ var _self = this; var _imgAll = $(c).parent().parent().find('.imgAll ul'); $(c).off().on('click',function(){ $(f).click(); $(f).off().on('change',function(){ var _this = this; _private.startUploadImg(_imgAll,_this.files,_self.MAX,_self.callback,_self.MW,_self.MH); }); }); } }; var _dataArr = []; var _private = { startUploadImg : function(o,files,MAX,callback,W,H){ _dataArr.length = 0; var _this = this; var fileImgArr = []; var lens = fileArr.length; if(lens > MAX ){ alert('不能大于'+MAX+'张'); return false; }; var lens = $(o).find('li').length ; if(lens >= MAX ){ alert('不能大于'+MAX+'张'); return false; }; for(var i=0,file;file=files[i++];){ fileArr.push(file); var reader = new FileReader(); reader.onload = (function(file){ return function(ev){ var image = new Image(); image.onload=function(){ var width = image.width; var height = image.height; fileImgArr.push({ fileSrc : ev.target.result, fileName : file.name, fileSize : file.size, height : height, width : width }); }; image.src= ev.target.result; }; })(file); reader.readAsDataURL(file); } //创建分时函数 var imgTimeSlice = _this.timeChunk(fileImgArr,function(file){ if(file.width > W || file.height > H){ alert('图片不能大于'+W+'*'+H+'像素'); return false; } //调用图片类 var up = new ImgFileupload(o,file.fileName,file.fileSrc,file.fileSize,callback); up.init(); },1); imgTimeSlice(); //调用分时函数 }, timeChunk : function(arr, fn, count) { var obj, t; var len = arr.length; var start = function() { for (var i = 0; i < Math.min(count || 1, arr.length); i++) { var obj = arr.shift(); fn(obj) } }; return function() { t = setInterval(function() { if (arr.length === 0) { return clearInterval(t); } start(); },200) } } }; var ImgFileupload = function(b,imgName,imgSrc,imgSize,callback){ this.b = b; this.imgName = imgName; this.imgSize = imgSize; this.imgSrc = imgSrc; this.callback = callback; }; var _delId = -1; //删除id用于判断删除个数 ImgFileupload.prototype.init =function() { _delId++; var _self = this; this.dom = document.createElement('li'); this.dom.innerHTML = ' <img src="/img/login.gif" alt="" data-src="'+this.imgSrc +'" class="imsg">' +' <i class="delImg">'+ ' X'+ ' </i>'; $(this.dom).attr({'data-delId':_delId,'data-delName':this.imgName}); $(this.b).append(this.dom); var _Img = new Image(); _Img.src = $(this.dom).find('img').attr('data-src'); _Img.onload = function(){ $(_self.dom).find('img').attr('src',_Img.src); } _dataArr.push({'delId' :_delId,src : this.imgSrc}); _self.callback(_dataArr); var _delAll = $(this.b).find('.delImg'); for(var i=0;i<_delAll.length;i++){ $(_delAll[i]).off().on('click',function(){ $(this).parent().fadeOut('slow',function(){ $(this).remove(); fileArr.splice(_delId,1); _delId --; }); var _deid = $(this).parent().attr('data-delId'); for(var n=0;n<_dataArr.length;n++){ if(_dataArr[n].delId == _deid){ _dataArr.splice(n,1); } } _self.callback(_dataArr) }); }; var _Imgpreview = $(this.b).find('img'); for(var k=0;k<_Imgpreview.length; k++){ $(_Imgpreview[k]).off().on('click',function(){ console.log($(this).attr('src')) }) } } win.ImgUploadeFiles = ImgUploadeFiles; })(window);
需要用到的两个图片,addUpload.png和login.gif(基本用不到)
在引入css和js后,需要在我们的js中定义一个全局变量:
<script type="text/javascript" th:inline="javascript"> var fileArr = []; </script>
这个fileArr是个对象数组,用于记录添加或删除文件,最终我们也是从这个数组中获取文件集合,提交给后台进行上传服务器处理。当然,聪明的你也可以修改imgFileupload.js,将fileArr换成你喜欢的名字。
接下来在你的html中添加上传图片的容器
<div class="imgupBox">
最后,初始化容器,指定图片上传个数,最大像素大小即可
<script type="text/javascript"> var imgFile = new ImgUploadeFiles('.imgupBox',function(e){ this.init({ MAX : 9, //限制个数 MH : 5800, //像素限制高度 MW : 5900, //像素限制宽度 callback : function(arr){ console.log(arr) } }); }); </script>
需要注意的是,由于html的加载顺序,初始化容器的代码,需要在引入的css,js和div容器之后,参考顺序如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link href="imgfileupload.css" rel="stylesheet"> </head> <body> <div class="imgupBox"></div> <script src="jquery.min.js"></script> <script type="text/javascript" src="imgFileupload.js"></script> <script type="text/javascript"> var imgFile = new ImgUploadeFiles('.imgupBox',function(e){ this.init({ MAX : 3, //限制个数 MH : 5800, //像素限制高度 MW : 5900, //像素限制宽度 callback : function(arr){ console.log(arr) } }); }); </script> </body> </html>
在选择上传图片完成后,最终的文件数组就是fileArr,我们通过ajax将文件传至后台:
function multipleFiles(){ //补充说明:因为我们给input标签设置multiple属性,因此一次可以上传多个文件 //获取选择图片数组 var files = fileArr; //获取选择图片的个数 var length = files.length; if(length == 0){ alert("请选择需要上传的图片!"); return; } var fu = new FormData(); for(var i=0;i<files.length;i++) { //循环获取多个文件 fu.append("file", files[i]); } $.ajax({ type: "post", url: "/uploadimgs", data: fu, cache: false, contentType: false, //不可缺参数 processData: false, //不可缺参数 success: function (data, status) { alert("图片上传成功!"); }, error: function (data) { alert("图片上传失败!"); }, complete: function () { } }); }
后台以springBoot为例,其他框架类似的,用文件类型进行接收:
@RequestMapping("/uploadimgs") public void defectAddUploadImgs(@RequestParam("file")MultipartFile[] files){ if (files!=null) { for(int i=0;i<files.length;i++) { //取出files[i],进行你的业务逻辑,上传oss或本地服务器 } } }
至此,一个朴实无华且枯燥的图片上传功能就完成了,恭喜发现此文的你可以踩点下班了~
分享可贵,如果觉得本文不错,有帮到你的话,点个赞呗 :)