javascript及php笔记:自己动手写一个ajax异步上传文件的jquery插件
前不久做了一个文件上传的功能页面,这次开发中我遇到了些问题,开发的时候由于时间比较紧张,因此有些细节做得并不是太仔细,所以最近几天将前面做得东西整理了下,在这里和大家 一起分享下。
首先我介绍下我所做的功能页面,页面会同时上传两个文件,一个是数据文件,一个是签名文件,而且数据文件一般都是比较大的,上传的同时文件的数据要同步解析同步校验,最终录入到数据库里,这就导致文件上传的时间都比较长,为了得到更好的用户体验,上传文件的时候页面做一个等待的遮罩效果,这个如果用ajax异步提交就能很好的作出效果,但是标准的ajax技术可以异步上传文件吗?答案是不行,至少标准的ajax技术里没有直接用来上传文件的方法,这时同事给了我一个项目组里使用过的ajax上传文件的插件,研究了下很让我失望,这个插件只能上传一个文件同时也不支持遮罩的效果,况且代码也超级烂:作为jQuery插件,代码里面居然很少使用jQuery方法,也不知道当时同事从那里找到了这个插件。
我现在对每一次能写js代码的机会都坚决把握,这次也不例外,我想自己写个ajax上传的jQuery插件。
ajax上传文件原理是一种用不可见的form和iframe框架模拟ajax异步提交的方式,我在制作的插件里添加了一个用于上传文件的form和iframe,其中form也是对用户不可见的,但是它是负责文件上传的form,iframe则是接收返回数据的载体,下面我把我页面的源码和页面上传文件时候的代码贴在下面,大家可以比较下:
源文件ajaxuploadfile.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Ajax File Upload PHP</title> </head> <script type="text/javascript" src="js/jquery-1.7.1.js"></script> <script type="text/javascript" src="js/jquery.ajaxupload.1.0.js"></script> <body> <form id="aupfFrm" name="aupfFrm" style="position: absolute;top:50px;left: 80px;"> <label for="username"> 用户姓名:</label> <input type="text" id="username" name="username"/> <br/> <label for="userpwd"> 用户密码:</label> <input type="password" id="userpwd" name="userpwd"/> <br/> <label for="uploadFile">请选择文件1:</label></label> <input type="file" id="uploadFile1" name="uploadFile1"/> <br/> <label for="uploadFile">请选择文件2:</label></label> <input type="file" id="uploadFile2" name="uploadFile2"/> <br/> <br/> <center> <input type="button" id="upBtn" name="upBtn" value="BUTTON"/> </center> </form> <span id="showErrInfo" style="color: red"></span> </body> </html> <script type="text/javascript"> $(document).ready(function(){ /* $("#upBtn").bind("click",function(){ $.ajaxFileUpload({ url:"php/uploadfile.php", loadPicUrl:"images/loading.gif", formElemIds:["username","userpwd","uploadFile1","uploadFile2"], dataType:"html", success:function(data){ $("body").html(data); }, error:function(data){ alert(data); } }); }); */ $("#upBtn").bind("click",function(){ $.ajaxFileUpload({ url:"php/uploadfile.php", loadPicUrl:"images/loading.gif", formElemIds:["username","userpwd","uploadFile1","uploadFile2"], dataType:"json", success:function(data){ $("#showErrInfo").text("");// 清空原来的提示信息 $("#showErrInfo").text("用户姓名:" + data.username + ";用户密码:" + data.userpwd + ";请求类型:" + data.dataType + ";提示信息:" + data.retTipInfo); }, error:function(data){ alert(data); } }); }); }); </script>
上传文件过程中的页面代码:
<form enctype="multipart/form-data" id="jqAjaxUploadFrom1344865748585" name="jqAjaxUploadFrom1344865748585" method="post" action="php/uploadfile.php" style="position: absolute; top: -1200px; left: -1200px;" target="jqAjaxUploadIframe1344865748585"> <input type="text" name="username" id="jqUploadFile1344865748585username"> <input type="password" name="userpwd" id="jqUploadFile1344865748585userpwd"> <input type="file" name="uploadFile1" id="jqUploadFile1344865748585uploadFile1"> <input type="file" name="uploadFile2" id="jqUploadFile1344865748585uploadFile2"> <input type="hidden" name="dataTp" id="dataTp" value="json"> </form> <iframe name="jqAjaxUploadIframe1344865748585" id="jqAjaxUploadIframe1344865748585" style="position: absolute; top: -1000px; left: -1000px;" src="php/uploadfile.php"> </iframe> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"> <title> Ajax File Upload PHP </title> <script src="js/jquery-1.7.1.js" type="text/javascript"> </script> <script src="js/jquery.ajaxupload.1.0.js" type="text/javascript"> </script> </head> <body> <form style="position: absolute;top:50px;left: 80px;" name="aupfFrm" id="aupfFrm"> <label for="username"> 用户姓名: </label> <input type="text" name="username" id="username"> <br> <label for="userpwd"> 用户密码: </label> <input type="password" name="userpwd" id="userpwd"> <br> <label for="uploadFile"> 请选择文件1: </label> <input type="file" name="uploadFile1" id="uploadFile1"> <br> <label for="uploadFile"> 请选择文件2: </label> <input type="file" name="uploadFile2" id="uploadFile2"> <br> <br> <center> <input type="button" value="BUTTON" name="upBtn" id="upBtn"> </center> </form> <span style="color: red" id="showErrInfo"> </span> <script type="text/javascript"> $(document).ready(function() { /* $("#upBtn").bind("click",function(){ $.ajaxFileUpload({ url:"php/uploadfile.php", loadPicUrl:"images/loading.gif", formElemIds:["username","userpwd","uploadFile1","uploadFile2"], dataType:"html", success:function(data){ $("body").html(data); }, error:function(data){ alert(data); } }); }); */ $("#upBtn").bind("click", function() { $.ajaxFileUpload({ url: "php/uploadfile.php", loadPicUrl: "images/loading.gif", formElemIds: ["username", "userpwd", "uploadFile1", "uploadFile2"], dataType: "json", success: function(data) { $("#showErrInfo").text(""); // 清空原来的提示信息 $("#showErrInfo").text("用户姓名:" + data.username + ";用户密码:" + data.userpwd + ";请求类型:" + data.dataType + ";提示信息:" + data.retTipInfo); }, error: function(data) { alert(data); } }); }); }); </script> <div id="up_overlay" style="display: block; height: 100%; width: 100%; left: 0px; top: 0px; position: fixed; z-index: 999; background-color: rgb(0, 0, 0); opacity: 0.59;"> </div> <img id="loadimg" style="left: 0px; top: 0px; margin-left: 280px; margin-top: 150px; position: fixed; z-index: 999; display: block;" src="images/loading.gif"> <form enctype="multipart/form-data" id="jqAjaxUploadFrom1344865748585" name="jqAjaxUploadFrom1344865748585" method="post" action="php/uploadfile.php" style="position: absolute; top: -1200px; left: -1200px;" target="jqAjaxUploadIframe1344865748585"> <input type="text" name="username" id="jqUploadFile1344865748585username"> <input type="password" name="userpwd" id="jqUploadFile1344865748585userpwd"> <input type="file" name="uploadFile1" id="jqUploadFile1344865748585uploadFile1"> <input type="file" name="uploadFile2" id="jqUploadFile1344865748585uploadFile2"> <input type="hidden" name="dataTp" id="dataTp" value="json"> </form> <iframe name="jqAjaxUploadIframe1344865748585" id="jqAjaxUploadIframe1344865748585" style="position: absolute; top: -1000px; left: -1000px;" src="php/uploadfile.php"> </iframe> </body> </html>
其中form代码:
<form enctype="multipart/form-data" id="jqAjaxUploadFrom1344865748585" name="jqAjaxUploadFrom1344865748585" method="post" action="php/uploadfile.php" style="position: absolute; top: -1200px; left: -1200px;" target="jqAjaxUploadIframe1344865748585"> <input type="text" name="username" id="jqUploadFile1344865748585username"> <input type="password" name="userpwd" id="jqUploadFile1344865748585userpwd"> <input type="file" name="uploadFile1" id="jqUploadFile1344865748585uploadFile1"> <input type="file" name="uploadFile2" id="jqUploadFile1344865748585uploadFile2"> <input type="hidden" name="dataTp" id="dataTp" value="json"> </form>
iframe代码:
<iframe name="jqAjaxUploadIframe1344865748585" id="jqAjaxUploadIframe1344865748585" style="position: absolute; top: -1000px; left: -1000px;" src="php/uploadfile.php"> </iframe>
遮罩的代码:
<div id="up_overlay" style="display: block; height: 100%; width: 100%; left: 0px; top: 0px; position: fixed; z-index: 999; background-color: rgb(0, 0, 0); opacity: 0.59;"> </div> <img id="loadimg" style="left: 0px; top: 0px; margin-left: 280px; margin-top: 150px; position: fixed; z-index: 999; display: block;" src="images/loading.gif">
调用方式:
$("#upBtn").bind("click",function(){ $.ajaxFileUpload({ url:"php/uploadfile.php", loadPicUrl:"images/loading.gif", formElemIds:["username","userpwd","uploadFile1","uploadFile2"], dataType:"json", success:function(data){ $("#showErrInfo").text("");// 清空原来的提示信息 $("#showErrInfo").text("用户姓名:" + data.username + ";用户密码:" + data.userpwd + ";请求类型:" + data.dataType + ";提示信息:" + data.retTipInfo); }, error:function(data){ alert(data); } }); });
参数介绍:
url:请求路径;
loadPicUrl:遮罩效果的等待图片;
formElemIds:提交到服务端的form元素的id值,注意一定要是个数组,如果那个字段的id没有写到数组里,那么它的数据也不会提交到服务端;
dataType:指定返回值的数据类型,我计划写html,json,xml和script,现在实现了html和script,其他两种也很简单,以后有空再补充;
success:文件上传成功的回调函数;
error:文件上传失败的回调函数。
下面就是我程序的源码了:
首先还是目录结果,如图:
javascript代码(jquery.ajaxupload.1.0.js):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 制作jQuery插件 author:xiajun add 2012-08-13 ajax多文件上传 ;(function($){ $.extend({ createUploadIframe:function(id,url){// 创建用于文件上传的iframe var upIframeId = "jqAjaxUploadIframe" + id; var jqIfrmObj = $('<iframe id="' + upIframeId + '" name="' + upIframeId + '" />'); // iframe的位置 jqIfrmObj.css('position','absolute'); jqIfrmObj.css('top','-1000px'); jqIfrmObj.css('left','-1000px'); jqIfrmObj.attr('src',url); $('body').append(jqIfrmObj); return jqIfrmObj; }, createUploadForm:function(timeId,fileElems,dataType){// 创建用于上传文件的form var upFormId = "jqAjaxUploadFrom" + timeId; var jqFormObj = $('<form action="" method="POST" name="' + upFormId + '" id="' + upFormId + '" enctype="multipart/form-data"></form>'); for (var i = 0,len = fileElems.length;i < len;i++){ var fileElemObj = fileElems[i], fileId = 'jqUploadFile' + timeId + fileElemObj, oldElem = $('#' + fileElemObj), newElem = $(oldElem).clone(); $(oldElem).attr('id',fileId); $(oldElem).before(newElem); $(oldElem).appendTo(jqFormObj); } // 数据返回类型 var dataTpIpt = $("<input type='hidden' id='dataTp' name = 'dataTp'/>"); dataTpIpt.val(dataType); jqFormObj.append(dataTpIpt); $(jqFormObj).css('position', 'absolute'); $(jqFormObj).css('top', '-1200px'); $(jqFormObj).css('left', '-1200px'); $(jqFormObj).appendTo('body'); return jqFormObj; }, createOverLay:function(){// 创建遮罩效果 var overLayDiv = $("<div></div>"); overLayDiv.attr("id","up_overlay"); overLayDiv.css("display","block"); overLayDiv.css("height","100%"); overLayDiv.css("width","100%"); overLayDiv.css("left","0px"); overLayDiv.css("top","0px"); overLayDiv.css("position","fixed"); overLayDiv.css("z-index","999"); overLayDiv.css("background-color","#000000"); overLayDiv.css("opacity","0.59"); overLayDiv.css("filter","Alpha(opacity=50)"); $("body").append(overLayDiv); }, createLoadingPic:function(loadPicUrl){// 创建正在加载的图片 var oLoadingPic = $("<img></img>"); oLoadingPic.attr("id","loadimg"); oLoadingPic.css("left","0px"); oLoadingPic.css("top","0px"); oLoadingPic.css("margin-left","280px"); oLoadingPic.css("margin-top","150px"); oLoadingPic.css("position","fixed"); oLoadingPic.css("z-index","999"); oLoadingPic.css("display","block"); oLoadingPic.attr("src",loadPicUrl); $("body").append(oLoadingPic); }, ajaxFileUpload:function(oJson){// 文件上传方法 // 遮罩效果 start jQuery.createOverLay(); jQuery.createLoadingPic(oJson.loadPicUrl); // 遮罩效果 end // 创建用于上传文件的iframe和form start var timeId = new Date().getTime(); var upfForm = jQuery.createUploadForm(timeId,oJson.formElemIds,oJson.dataType); upfIframe = jQuery.createUploadIframe(timeId,oJson.url), upfFormId = "jqAjaxUploadFrom" + timeId, upfIframeId = "jqAjaxUploadIframe" + timeId; // 创建用于上传文件的iframe和form end // 上传文件的回调函数 start var xml = {}; var uploadFileCallback = function(){ var cbUpfIfrm = $("#" + upfIframeId); try{ xml = cbUpfIfrm.contents().find("body"); var sErrFlag = $.trim(cbUpfIfrm.contents().find("body").find("input[id='errFlag']").val()), sTipInfo = $.trim(cbUpfIfrm.contents().find("body").find("input[id='errInfo']").val()); if (sErrFlag == 'false'){ if (oJson.error){ oJson.error(sTipInfo); } }else if (sErrFlag == 'true'){ var retData = jQuery.uploadRetHttpData(xml,oJson.dataType); if (oJson.success){ $("#up_overlay").remove(); $("#loadimg").remove(); oJson.success(retData); } } $(cbUpfIfrm).unbind(); setTimeout(function(){ $(upfIframe).remove(); $(upfForm).remove(); },100); xml = null; }catch(e){ alert("Callback error1:" + e); } } // 上传文件的回调函数 end try{ var ajaxUpfForm = $("#" + upfFormId); ajaxUpfForm.attr("action",oJson.url); ajaxUpfForm.attr("method","post"); ajaxUpfForm.attr("target",upfIframeId); if (ajaxUpfForm.attr("encoding")){ ajaxUpfForm.attr("encoding","multipart/form-data"); }else{ ajaxUpfForm.attr("enctype","multipart/form-data"); } ajaxUpfForm.submit(); }catch(e){ alert("ajaxFileUpload Error:" + e); } $("#" + upfIframeId).bind("load",uploadFileCallback); return {abort: function () {}}; }, uploadRetHttpData:function(resp,type){// 上传返回的数据 switch (type) { case 'html': return resp.find("div[id='resultData']").html(); break; case 'xml': alert("to do ....");// 以后再写 break; case 'json': return eval("(" + resp.find("div[id='resultData']").text() + ")"); break; case 'script': alert("to do ....");// 以后再写 break; default: break; } } }); })(jQuery);
服务端我使用的是php,我的工作语言是java,今天使用php也是第一次尝试另一种服务端语言,这个决定也不是临时的,我打算以后自我学习所写的代码都将会是脚本语言,最近一段时间是php,等php熟练后就是python,我的计划是学会四门脚本语言,另外两种是nodeJs和ruby了。
使用php写代码的感觉就是爽,比java方便多了,做网站就应该用php这样的脚本语言。
下面是我服务端源码(uploadfile.php):
<?php //sleep(234); /** * 上传错误处理函数 * Enter description here ... * @param $errCode */ function parseFileErr($fileNm,$errCode){ setOperateFlag('false'); switch ($errCode) { case 1: echo $fileNm . ":文件大小超过了PHP.ini中的文件限制!"; break; case 2: echo $fileNm . ":文件大小超过了浏览器限制!"; break; case 3: echo $fileNm . ":文件部分被上传!"; break; case 4: echo $fileNm . ":没有找到要上传的文件!"; break; case 5: echo $fileNm . ":服务器临时文件夹丢失,请重新上传!"; break; case 6: echo $fileNm . ":文件写入到临时文件夹出错!"; break; default: echo $fileNm . ":文件上传错误!!"; break; } } /** * 设置操作状态 true表示成功 false 表示失败 * Enter description here ... * @param $flag */ function setOperateFlag($flag,$tipInfo){ echo "<input type='hidden' id='errFlag' name='errFlag' value='" . $flag . "' />"; echo "<input type='hidden' id='errInfo' name='errInfo' value='" . $tipInfo . "' />"; } /** * 校验文件的类型和大小 * Enter description here ... * @param $upFileTp * @param $upFileSize */ function checkFileTypeAndSize($upFileTp,$upFileSize){ if ($upFileSize < 28480){ return true; }else{ return false; } } /** * 判断上传文件是否存在 * Enter description here ... * @param $upFileNm */ function checkFileExists($upFileNm){ if (file_exists($upFileNm)){ return "tp0_" . $upFileNm;// 已存在文件改名字保存 }else{ return $upFileNm; } } /** * 返回数据 * Enter description here ... */ function resultDataFtn(){ $reqUserNm = $_REQUEST["username"]; $reqUserPwd = $_REQUEST["userpwd"]; $reqDataTp = $_REQUEST["dataTp"]; echo "<div id='resultData'>"; if ($reqDataTp == 'html'){ echo "<h1 style='color:red'>PHP Ajax 多文件上传 成功</h1>"; }elseif ($reqDataTp == 'json'){ $jsonArr = array("username"=> $reqUserNm, "userpwd" => $reqUserPwd, "dataType" => $reqDataTp, "retTipInfo" => "PHP Ajax 多文件上传 成功"); echo json_encode($jsonArr); } echo "</div>"; } // 上传文件1 $upFile01Name = $_FILES['uploadFile1']['name']; $upFile01Size = $_FILES['uploadFile1']['size']; $upFile01TmpNm = $_FILES['uploadFile1']['tmp_name']; $upFile01Type = $_FILES['uploadFile1']['type']; $upFile01Err = $_FILES['uploadFile1']['error']; // 上传文件2 $upFile02Name = $_FILES['uploadFile2']['name']; $upFile02Size = $_FILES['uploadFile2']['size']; $upFile02TmpNm = $_FILES['uploadFile2']['tmp_name']; $upFile02Type = $_FILES['uploadFile2']['type']; $upFile02Err = $_FILES['uploadFile2']['error']; if ($upFile01Name == '' || $upFile02Name == ''){ setOperateFlag('false',"请上传文件!"); //echo "<script>alert('请上传文件!');</script>"; }else{ if ($upFile01Err > 0 || $upFile02Err > 0){ //$upFile01Err > 0 ? parseFileErr($upFile01Name, $upFile01Err) : ""; //$upFile02Err > 0 ? parseFileErr($upFile02Name, $upFile02Err) : ""; setOperateFlag('false',"上传失败!"); }else{ if (!checkFileTypeAndSize($upFile01Type, $upFile01Size) || !checkFileTypeAndSize($upFile02Type, $upFile02Size)){ setOperateFlag('false','上传的文件过大!'); //echo "<script>alert('上传的文件过大!');</script>"; }else{ setOperateFlag('true',"成功"); move_uploaded_file($upFile01TmpNm, checkFileExists($upFile01Name)); move_uploaded_file($upFile02TmpNm, checkFileExists($upFile02Name)); resultDataFtn();// 结果数据处理 } } } ?>
这个插件写的很匆忙,问题肯定很多,特别是服务端和客户端有些逻辑有一定耦合,我估计某些童鞋想把插件迁移到其他的服务端上会有点麻烦,等有时间我会进一步改进自己的代码。
源代码下载路径:
https://files.cnblogs.com/sharpxiajun/ajaxfileupload1.0.zip