最近项目在搞重构,一个很重要的图片上传功能之前是上个前端从网上找的严重不支持ie8,ie9;而且产品经理

还对上传插件提了好多新的要求,然后,插件被改的面目全非,而且改的很痛苦;现在终于有了时间重构;

自己花了三天不到时间写了一个图片上传插件(也不能算是插件,因为可能只是符合我们现有的要求,而且通用性不太好)。

这里分享给大家,不是让大家拿过去用;而是梳理上传的思路,分享自己写代码过程中的一些心得体会等等。

整体来说插件要支持以下几个功能:

1.指定格式,指定数量上传;

2.支持本地预览已选择图片

3.支持终止上传,继续上传;

4.支持度图片的删除,重新选择等等。

5.还有进度条,上传完成的数量,状态信息等等。。

还有一个一些诸如,当前文件夹满了给出提示,并上传到新的文件夹等等。。其实自己一开始想的比较简单;因为产品的流程图

页想下面一样简单;各种异常情况;规格都没有。

第一步当然是画页面,搭架子,留接口。界面大概如下:

为了方便下面说明我先直接贴源码和调用示例

define(['min_win','uploadPreview'], function (min_win, uploadPreview) {function Upload() {	if(this instanceof Upload){		this.config = {			maxCount:20,//最大批量上传数量			dragable:false,//是否支持拖拽			formats:["jpg", "jpeg", "png"],//支持的图片格式			onChange:null,//选中的回调			onSubmit:null,//确认上传回调			onDelete:null,//删除回调			onSuccess:null,//单个文件成功的回调			onComplete:null,//完成本次上传的回调,(全部成功不会触发)			onFailure:null,//单个失败回调			onAllSuccess:null,//全部成功的回调			OnUpload:null,//单个文件准备上传前的回调,			onClose:null,//关闭弹窗回调			url:"",//上传的url			htmlStr:""//自定义html片段		};		this.handlers = {};//自定义事件集合		this.filesArr = [];		this.fileSuccessCount = 0;//上传成功个数		this.fileFailureCount = 0;//上传失败个数		this.filesFailureArr = [];		this.uploadFlag = false;//是否继续上传		this.isComplete = false;		this.isAllSuccess = false;		this.uploadStatus = 0;//0,表示为开始,1表示进行时,-1表示完成	} else{		return new Upload();	}}/*Upload.prototype = new window.Widget();*/Upload.prototype.run = function (config) {    var CFG = $.extend(this.config, config);//合并对象    this.initData();    this.renderUI(CFG);    this.syncUI(CFG);    this.bindUI(CFG);    return this;}Upload.prototype.initData = function(){	this.handlers = {};	this.filesArr = [];	this.clearFilesCount();	this.filesFailureArr = [];	this.uploadFlag = false;//是否继续上传	this.uploadStatus = 0;}Upload.prototype.renderUI = function (CFG) {	new min_win.min_window({	    type:{	        mack:true,	        position:'center'	    },	    html:CFG.htmlStr,	    callback:null	}).run();}Upload.prototype.bindUI = function(CFG){	var that = this;	$("#ul-fileInput").on("change", changeHandler);	$(".uploadSubmit").bind("click", uploadHandler);	$(".continueSelect").bind("click", continueHandler);	$(".reSelect").bind("click", reSelectHandler);	$("body").on("click", ".ul-waitItem .ul-curIcon", deleteHandler);	$(".u-uploadDailog .ul-closeBut").bind("click", closeHandler);	//关闭事件	function closeHandler(event){		CFG.onClose && CFG.onClose();	}	//选中change事件	function changeHandler(event){		console.log("changeHandler");		//如果支持html5的this.files		if(this.files){			var itemListHtml ="";			var len = this.files.length;			for(var i = 0;i < len; i++){				var tempIndex = 0;				var tempfile = this.files[i];				if(that.checkFormat(tempfile.name, that.config.formats)){					if(that.filesArr.length){						tempIndex = that.filesArr.length;					}					//文件数量>maxCount,只取前maxCount个					if(that.filesArr.length < that.config.maxCount){						itemListHtml +=  '
'; tempfile.index = tempIndex;//每个file对象打上唯一标示,与ul-item一一对应 tempfile.sendtype = "h5"; that.filesArr.push(tempfile); } } } $(".ul-imgCont").append(itemListHtml); that.setButHideOrShow($(".continueSelect"), true);//展示继续上传按钮 } else { var fileVal = $("#ul-fileInput").val(); if(!that.checkFormat(fileVal, that.config.formats)){ return; } var itemHtmlStr = '
';   $(".ul-imgCont").append(itemHtmlStr);   var $img = $(".ul-item img");  try {    $img.attr('src', getImgPath(this.files[0])); } catch (e) {     var src = "";     var obj = $img;     var div = obj.parents(".ul-waitItem");     var curDom = document.getElementById("ul-fileInput");     curDom.select();     curDom.blur();     src = document.selection.createRange().text;      document.selection.empty();       div.css({         'filter': 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'     });          div[0].filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = src; var fileObj = {};     fileObj.sendtype = "ie";     fileObj.src = src;     fileObj.index = 0;     that.filesArr.push(fileObj); } } //选择文件后的回调 CFG.onChange && CFG.onChange(); } //继续选择 function continueHandler(event){ //如果当前按钮处于禁用状态,直接返回 if($(this).hasClass("ul-input-disable")){ return; } $("#ul-fileInput").click(); } //上传图片 function uploadHandler(event) { //如果当前按钮处于禁用状态,直接返回 if($(this).hasClass("ul-input-disable")){ return; } //点击终止上传 if($(this).hasClass("ul-input-stopUpload")){ that.stopUpload(); //终止后,为上传图片全部标记为失败 $(".ul-waitItem").addClass("ul-failureItem"); $(".ul-waitItem").removeClass("ul-waitItem"); that.setButText($(".uploadSubmit"), '继续上传'); that.setButHideOrShow($(".continueSelect"), false);//隐藏继续选择 that.setButHideOrShow($(".reSelect"), true);//显示重新选择 $(".uploadSubmit").addClass("ul-input-goOnUpload"); $(this).removeClass("ul-input-stopUpload"); return; } //点击继续上传 if($(this).hasClass("ul-input-goOnUpload")){ //that.clearFilesCount(); that.goOnUpload(); } //点击上传的回调 CFG.onSubmit && CFG.onSubmit(); that.uploadSingle(); } //预览时,删除指定图片 function deleteHandler(event){ if(1=== that.uploadStatus){ return; } var parentItem = $(this).parents(".ul-waitItem"); var index = parseInt(parentItem.attr("index"), 10); delArrItemByIndex("index", index, that.filesArr); that.setSelectMsg(); parentItem.remove(); //删除文件后的回调 CFG.onDelete && CFG.onDelete(); } //重新选择 function reSelectHandler(event){ //如果当前按钮处于禁用状态,直接返回 if($(this).hasClass("ul-input-disable")){ return; } that.clearDailog(); that.clearBar(); that.clearUploadMsg(); that.clearSelectMsg(); that.initData(); }}//图片格式校验Upload.prototype.checkFormat = function(urlStr, array){ if(!urlStr || "" === urlStr){ return false; } var fromats = urlStr.substring(urlStr.lastIndexOf(".")+1,urlStr.length) if($.inArray(fromats.toLowerCase(), array) > -1){ return true; } return false;}//修改当前上传的状态:0,表示为开始,1表示进行时,-1表示完成Upload.prototype.setUploadStatus = function(status){ this.uploadStatus = status;}Upload.prototype.setFilesFailure = function(){ }//销毁插件Upload.prototype.destroy = function(){ this.initData(); $(".mack_cont").remove(); $(".mack").remove();}//修改上传urlUpload.prototype.updateUrl = function(newUrl){ this.config.url = newUrl;}//获取文件的个数Upload.prototype.getFileArr = function(){ return this.filesArr;}//还原到初始布局Upload.prototype.clearDailog = function(){ $(".ul-item").remove(); $(".ul-imgCont").css({ "text-align":"center" }); $(".ul-filepickerWrap").show(); $(".ul-uploadMsg p").hide(); this.setButHideOrShow($(".ul-input-enable"), false); this.setButHideOrShow($(".uploadSubmit"), true); $(".uploadSubmit").removeClass("ul-input-goOnUpload"); this.setButText($(".uploadSubmit"), '确认上传');}//清空进度条Upload.prototype.clearBar = function(){ $(".ul-bar-cur").animate({width:20},250); $(".ul-bar-tips").text("0%");}//设置进度条100%Upload.prototype.fullBar = function(){ $(".ul-bar-cur").animate({width:450},250); $(".ul-bar-tips").text("100%");}//清空上传成功失败提示信息Upload.prototype.clearUploadMsg = function(){ $(".ul-successCount").text("0"); $(".ul-errorCount").text("0");}//清空上传已选文件信息Upload.prototype.clearSelectMsg = function(){ $(".ul-selectcount").text("0"); $(".ul-selectsize").text("0" + "KB"); $(".ul-selectleftCount").text("30");}//修改状态为:停止上传Upload.prototype.stopUpload = function(){ this.uploadFlag = true;}//修改状态为:继续上传Upload.prototype.goOnUpload = function(){ this.uploadFlag = false;}//设置上传成功失败,提示信息Upload.prototype.setUploadMsg = function(){ $(".ul-successCount").text(this.filesArr.length); $(".ul-errorCount").text(this.fileFailureCount);}//设置已选文件个数,及容量Upload.prototype.setSelectMsg = function (){ var len = this.filesArr.length; var size = 0; for(var i = 0;i < len; i++){ var temp = this.filesArr[i]; if("ie" === temp.sendtype){ //var filePath = $("#ul-fileInput").val();                     //var fileSystem = new ActiveXObject("Scripting.FileSystemObject");            //var file = fileSystem.GetFile (filePath);                        //size += file.Size;    } size += temp.size; } var sizeVal = size/1000; var unit = "KB"; if(sizeVal >= 1000 ){ sizeVal = sizeVal/1000; unit ="M"; } $(".ul-selectcount").text(len); $(".ul-selectleftCount").text(this.config.maxCount - len); $(".ul-selectsize").text(sizeVal.toFixed(2) + unit);}//清除上传失败和成功个数Upload.prototype.clearFilesCount = function(){ this.fileFailureCount = 0; this.fileSuccessCount = 0;}//刷新进度条Upload.prototype.resetBarByOnce = function (that){ var curlen = this.fileFailureCount + this.fileSuccessCount; var totalLen = this.getFilesLen(); var newPrecent = curlen/totalLen; $(".ul-bar-tips").text(Math.floor(newPrecent*100) + "%"); $(".ul-bar-cur").animate({ width:Math.floor(newPrecent*450) },250);}//根据返回结果,调整图片的区域和状态Upload.prototype.LayoutDailog = function(file, data){ var index = file.index; var status= data.status; var curItem = $(".ul-imgCont [index='" + index + "']"); if(status){   curItem.removeClass("ul-waitItem");   curItem.removeClass("ul-failureItem");   curItem.addClass("ul-scItem");      var htmlStr = $(".ul-imgCont [index='" + index + "']")[0].outerHTML;      $(".ul-list").append(htmlStr);      if("ie" === file.sendtype){        $(".ul-list [index='" + index + "']")[0].filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = file.src;      }      curItem.remove();      return; }    curItem.removeClass("ul-waitItem");    curItem.addClass("ul-failureItem");}//获取本次上传文件的数量Upload.prototype.getFilesLen = function(){ return $(".ul-item").length;}//获取本次上传失败的文件数量Upload.prototype.getfailureFilesLen = function(){ return $(".ul-failu").length;}//递归调用,逐个上传文件Upload.prototype.uploadSingle = function (uploadCallBack){ var that = this; //文件列表为空返回 if( 0 === this.filesArr.length || !this.filesArr[0]){ this.isComplete = true; this.uploadStatus = -1; this.resetErrorCount(); //全部上传成功 if(this.getFilesLen() === this.fileSuccessCount){ this.isAllSuccess = true; this.config.onAllSuccess && this.config.onAllSuccess(); } else {//部分成功 this.filesArr = this.filesFailureArr;//一次上传完成后,将fileArr重置为当前失败的fileArr this.config.onComplete && this.config.onComplete(); } return; } if(this.uploadFlag){//上传被终止,直接返回 return; } var file = this.filesArr[0]; var sendtype = file.sendtype; var CFG = this.config; this.uploadStatus = 1; uploadCallBack && uploadCallBack();//上传前的回调 if("h5" === sendtype){ var formdata = new FormData(); formdata.append("fileList", file);      $.ajax({           url: CFG.url,           type: 'POST',           data: formdata,           async: true,           cache: false,           contentType: false,           processData: false,           success: function (data) {            that.monitorResponse(file, data);            that.uploadSingle();           },           error: function (data) {            //that.filesArr.shift();             that.filesFailureArr.push(file);             that.uploadSingle();           }      });      return; } else { //ie处理文件上传 window.frameCount = 0; var frameName = 'upload_frame_' + (frameCount++); var iframe = $('
').attr('name', frameName); $("#ul-uploadForm").attr("target", frameName); $("#ul-uploadForm").attr("action", CFG.url); $("body").append(iframe); $("#ul-uploadForm").submit(); //监听隐藏iframe的load状态,更新上传信息 $("[name=" + frameName + "]").load(function(){ var data = JSON.parse($("#upload-iframe").contents().find("body").html()); that.monitorResponse(file, data); if(data.status){ that.isAllSuccess = true; that.config.onAllSuccess && that.config.onAllSuccess(); } }); }}//删除对象数组中属性property值为val的,那一项function delArrItemByIndex(prop, val, arr){ if("number" !== typeof val || !arr){ alert("传入参数不合法"); return; } var len = arr.length; for(var i = 0; i < len; i++){ var temp = arr[i]; if(temp[prop] === val){ arr.splice(i, 1); return; } }}//监听每一次ajax请求返回的结果Upload.prototype.monitorResponse = function(file, data){ this.calculatFileCount(data.status); this.resetBarByOnce(); if(data.status){ this.filesArr.shift(); //console.log("当前剩余文件:" + this.filesArr.length); this.config.onSuccess && this.config.onSuccess(file, data); } else {  this.filesFailureArr.push(file);  //this.filesArr.shift();  //resetErrorCount(true);  this.config.onFailure && this.config.onFailure(file.index, data); }}//设置成功,失败文件个数Upload.prototype.calculatFileCount = function(status){ if(status){ this.fileSuccessCount++; return; } this.fileFailureCount++;}//禁用按钮Upload.prototype.setButDisable = function($element){ $element.removeClass("ul-input-enable"); $element.addClass("ul-input-disable");}//激活按钮Upload.prototype.setButEnable = function($element){ $element.removeClass("ul-input-disable"); $element.addClass("ul-input-enable");}//修改按钮文本Upload.prototype.setButText  = function($element, str){ $element.text(str);}//隐藏显示按钮Upload.prototype.setButHideOrShow = function($element, status){ if(status){ $element.show(); return; } $element.hide();}//重置成功,失败文件个数Upload.prototype.resetErrorCount = function (){ $(".ul-scCountMsg").show(); $(".ul-successCount").text(this.getFilesLen() - this.getfailureFilesLen()); if(this.getfailureFilesLen() > 0){//失败个数,仅在有失败情况,显示 $(".ul-errorCountMsg").show(); $(".ul-errorCount").text(this.getfailureFilesLen()); return; } return;}//得到图片的完整路径,兼容性处理function getImgPath(file) {    var url = null;    if (window.createObjectURL != undefined) {        url = window.createObjectURL(file)    } else if (window.URL != undefined) {        url = window.URL.createObjectURL(file)    } else if (window.webkitURL != undefined) {        url = window.webkitURL.createObjectURL(file)    }    return url;}Upload.prototype.syncUI = function(CFG){}    return { Upload: Upload };});

调用示例:

   //新增文件    $("body").on("click",".add_file", addFileHandler);//确认上传到其他文件夹    function uploadToOtherSubmit(event){        var floderId = $('.checkboxitem:checked').val();        var floderType = $(".moveFileDailog .check").attr("tab");        var newUrl = '/CloudUpload/upLoad/folder_id/' + floderId + '/type/' + tab_type + '.html';        uploader.updateUrl(newUrl);        uploader.goOnUpload();        uploader.clearBar();        uploader.clearFilesCount();        uploader.uploadSingle(uploadCallback);    }    function uploadCallback() {        $(".moveFileDailog .j_win_close").click();    }    var uploader = null;    function addFileHandler(event){        uploader = new upload.Upload().run({            url:'/CloudUpload/upLoad/folder_id/' + folder_id + '/type/' + folder_type + '.html',            htmlStr:uploadDailogHtml,            maxCount:30,            formats:["jpg", "jpeg", "png"],            onChange:function(){                if(uploader.filesArr.length){                    $(".ul-filepickerWrap").hide();                    $(".ul-imgCont").css({                        "text-align":"left"                    });                    uploader.setSelectMsg();                    uploader.setButEnable($(".continueSelect"));                    uploader.setButHideOrShow($(".continueSelect"), true);                }            },            onSubmit:function(){                var len = uploader.filesArr.length;                if(!len){                    cloudUtil.cloudMsg("请选择文件.", null);                }                uploader.clearUploadMsg();                uploader.clearBar();                uploader.setUploadMsg();                uploader.setButDisable($(".continueSelect"));//禁用继续选择按钮                uploader.setButText($(".uploadSubmit"), '终止上传');                $(".uploadSubmit").addClass("ul-input-stopUpload");            },            onSuccess:function(file, data){                 uploader.LayoutDailog(file, data);            },            onComplete:function(){                uploader.clearFilesCount();                uploader.setButHideOrShow($(".continueSelect"), false);//隐藏继续选择                uploader.setButHideOrShow($(".reSelect"), true);//显示重新选择                uploader.setButText($(".uploadSubmit"), '继续上传');            },            onAllSuccess:function(){                uploader.setButHideOrShow($(".continueSelect"), false);//隐藏继续选择                uploader.setButHideOrShow($(".reSelect"), false);//隐藏重新选择                uploader.setButText($(".uploadSubmit"), '上传完成');                $(".ul-waitItem").remove(); //清空预览区域                uploader.setButDisable($(".uploadSubmit"));//禁用上传                setTimeout(function(){                    uploader.destroy();                    window.location.reload();                }, 2000);            },            onClose:function(){               if(1 === uploader.uploadStatus){                uploader.stopUpload();//文件夹已满时,停止                var length = 0;                min_win.comm_remind({                    html:"文件未上传完,是否关闭?",                    yes_callback:function(){                        uploader.destroy();                        $(".mack_cont").eq(length-1).find(".j-win_no").click();                        window.location.reload();                    },                    callback:function(){                        length = $(".mack_cont").length;                        $(".mack_cont").eq(length-1).find(".j-win_no").addClass("j_win_close");                    },                    no_callback:function(){                        uploader.goOnUpload();                        uploader.uploadSingle(null);                    }                }).run();               }else{                                    uploader.destroy();               }            },            onFailure:function(index, data){                switch(data.errorCode){                    case 401:                        cloudUtil.cloudMsg("个人云盘空间不足");                        uploader.stopUpload();//空间不足时,停止上传                        uploader.fullBar();                        uploader.setFilesFailure();                        ploader.setUploadStatus(-1);                    break;                    case 402:                        uploader.stopUpload();//文件夹已满时,停止                        uploader.fullBar();                        uploader.setFilesFailure();                        uploader.setUploadStatus(-1);                        var length = 0;                        fullRemind = min_win.comm_remind({                            html:'
该文件夹已满,是否新建主题
文件夹来存储剩余的图片?

',                            yes_callback:function(){                                $(".mack_cont").eq(length-1).find(".j_win_close").click();                                uploadFileToOthers();                            },                            callback:function(){                                var length = $(".mack_cont").length;                                $(".mack_cont").eq(length-1).find(".j-win_no").addClass("j_win_close");                            }                        }).run();                    break;                    case 403:                         uploader.LayoutDailog(file, data);                        //console.log("上传的文件过大");                    break;                    case 405:                        uploader.LayoutDailog(file, data);                        //console.log("文件格式不允许");                    break;                    case 406:                        uploader.LayoutDailog(file, data);                        //console.log("生成缩略图失败");                    break;                    case 407:                        uploader.LayoutDailog(file, data);                        //console.log("文件上传失败407");                    break;                }            }        });    }

参与含义就不一一说明,注释很清楚的。

然后首先实现的是,图片的预览功能(支持IE8)

IE8,9由于所谓的安全机制(网友都这么说,人云亦云)所以要使用滤镜实现预览;

通过捕获IE8,9的异常然后使用滤镜实现预览

需要主要的是,这行代码:

 var curDom = document.getElementById("ul-fileInput");

curDom.select();

如果不选中,src始终为空;也就实现不了滤镜预览图片

1.由于插件按钮众多,状态也比较多所以提供了几个接口来操作按钮的显示隐藏,文件,状态(是否禁用)等。

setButDisable,setButEnable,setButText,setButHideOrShow

2.同样由于支持终止,继续上传;需要提供两个接口来操作上传的状态

stopUpload,goOnUpload

3.还有就是支持文件夹满时,上传到其他文件夹,所以需要提供一个更新url的接口

当然还有一些其他接口就不一一说明。

需要重点说明的是,IE8,9是采用表单提交方式;为了保证为了上传图片成功后,不刷新页面;使用了一个隐藏的iframe获取上传结果;

并监听iframede load事件获取上传的响应信息

//ie处理文件上传window.frameCount = 0;var frameName = 'upload_frame_' + (frameCount++);var iframe = $('
').attr('name', frameName);$("#ul-uploadForm").attr("target", frameName);$("#ul-uploadForm").attr("action", CFG.url);$("body").append(iframe);$("#ul-uploadForm").submit();//监听隐藏iframe的load状态,更新上传信息$("[name=" + frameName + "]").load(function(){var data = JSON.parse($("#upload-iframe").contents().find("body").html());that.monitorResponse(file, data);if(data.status){that.isAllSuccess = true;that.config.onAllSuccess && that.config.onAllSuccess();}});

效果图如下:

  1. 上传:

  2. 终止上传:

  3. 继续上传,上传完成

  4. 文件满了:

  5. 正在上传时,关闭弹窗

大概效果就是这样,总结总结。