1. 级联下拉列表
例1.1 级联下拉列表。
(1)编写AjaxRequest.js文件,并将其保存到JS文件夹中。AjaxRequest.js的具体代码如下:
var net = new Object(); //定义一个全局变量net //编写构造函数 net.AjaxRequest=function(url,onload,onerror,method,params){ this.req = null; this.onload = onload; this.onerror=(onerror)?onerror:this.defaultError; this.loadDate(url,method,params); } //编写用于初始化XMLHttpRequest对象并指定处理函数,最后发送HTTP请求的方法 net.AjaxRequest.prototype.loadDate=function(url,method,params){ if(!method){ method="GET"; } if(window.XMLHttpRequest){ this.req=new XMLHttpRequest(); }else if(window.ActiveXObject){ this.req = new ActiveXObject("Microsoft.XMLHTTP"); } if(this.req){ try{ var loader = this; this.req.onreadystatechange=function(){ net.AjaxRequest.onReadyState.call(loader); } this.req.open(method,url,true); //建立对服务器的调用 if(method=="POST"){ this.req.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //设置请求头 } this.req.send(params); }catch(err){ this.onerror.call(this); } } } //重构回调函数 net.AjaxRequest.onReadyState = function(){ var req=this.req; var ready=req.readyState; if(ready==4){ if(req.status==200){ this.onload.call(this); }else{ this.onerror.call(this); } } } //重构默认的错误处理函数 net.AjaxRequest.prototype.defaultError = function(){ alert("错误数据 回调状态:"+this.req.readyState+" 状态:"+this.req.status); }
(2)编写index.jsp文件,并在该文件中包含AjaxRequest.js文件,具体代码如下:
<script language="javascript" src="JS/AjaxRequest.js"></script>
(3)在index.jsp页面中编写错误处理的函数、实例化Ajax对象的方法和回调函数。在本例中,涉及两次异步操作,所以需要编写两个实例化Ajax对象的方法和回调函数。
编写实例化用于异步获取省份和直辖市的Ajax对象的方法和回调函数。具体代码如下:
function getProvince(){ var loader=new net.AjaxRequest("ZoneServlet?action=getProvince&nocache="+new Date().getTime(), deal_getProvince, onerror, "GET"); } function deal_getProvince(){ //通过循环将数组中的省份名称添加到下拉列表中 provinceArr=this.req.responseText.split(","); //将获取的省份名称字符串分割为数组 for(i=0;i<provinceArr.length;i++){ document.getElementById("province").options[i]=new Option(provinceArr[i],provinceArr[i]); } if(provinceArr[0]!=""){ getCity(provinceArr[0]); //获取市县 } } window.onload=function(){ getProvince(); }
编写实例化用于异步获取市县的Ajax对象的方法和回调函数,以及错误处理函数。具体代码如下:
function getCity(selProvince){ var loader=new net.AjaxRequest("ZoneServlet?action=getCity&parProvince="+selProvince+"&nocache="+new Date().getTime(), deal_getCity, onerror, "GET"); } function deal_getCity(){ cityArr=this.req.responseText.split(","); //将获取的市县名称字符串分割为数组 document.getElementById("city").length=0; //清空下拉列表 for(i=0;i<cityArr.length;i++){ document.getElementById("city").options[i]=new Option(cityArr[i],cityArr[i]); } } function onerror(){} //错误处理函数
(4) 在页面中添加设置省份和直辖市的下拉列表,名称为province和设置市县的下拉列表,名称为city,并在省份和直辖市下拉列表的onchange事件中,调用getCity()方法获取省份对应的市县。具体代码如下:
<select name="province" id="province" onchange="getCity(this.value)"></select> <select name="city" id="city"></select>
(5)编写获取居住地的Servlet实现类ZoneServlet,在该Servlet中的doGet()方法中,编写以下代码用于根据传递的action参数,执行不同的处理方法。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); //获取action参数的值 if("getProvince".equals(action)){ //获取省份和直辖市信息 this.getProvince(request,response); }else if("getCity".equals(action)){ //获取市县信息 this.getCity(request,response); } }
(6)在ZoneServlet中,编写getProvince()方法。在该方法中,将省份信息连接为一个以逗号分隔的字符串输出到页面上(本例比较简单实际当中应采用Map存放省份与市县信息)。具体代码如下:
/** * 获取省份和直辖市 * @param request * @param response * @throws ServletException * @throws IOException */ public void getProvince(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); String result="吉林"; response.setContentType("text/html"); PrintWriter out=response.getWriter(); out.print(result); //输出获取的省份字符串 out.flush(); out.close(); }
(7)在ZoneServlet中,编写个getCity()方法。在该方法中,中获取指定省份对应的市县信息,并将获取的是市县信息连接成一个以逗号分隔的字符串输出到页面上。具体代码如下:
/** * 获取市县 * @param request * @param response * @throws ServletException * @throws IOException */ public void getCity(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); String result="长春,延边,白山,白城,四平,辽源"; response.setContentType("text/html"); PrintWriter out=response.getWriter(); out.print(result); //输出获取的市县字符串 out.flush(); out.close(); }
(8) 为了在页面载入后显示默认的省份,还需要在页面的onload事件中调用获取省份的方法getProvince()。具体代码如下:
window.onload=function(){
getProvince(); //获取省份和直辖市
}
运行本实例index.jsp,结果如下所示:
2. 显示进度条
文件上传是一个很费时的任务,经常需要用户进行长时间等待,为了让用户在等待的过程中,即使了解上传的进度,可以在进行文件上传时,显示上传进度条。下面将介绍如何实现带进度条的文件上传。
例2.1 显示进度条。
(1) 编写index.jsp页面,在该页面中添加用于获取上传文件所需信息的表单及表单元素。由于要实现文件上传,所以需要将表单的enctype属性设置为multipart/form-data。关键代码如下:
<form name="form1" enctype="multipart/form-data" method="post" action="UpLoad?action=uploadFile">
<div align="center">
请选择要上传的文件:<input type="file" name="file" size="42"><br>
<input type="button" name="shangchuan" value="上传" width="61" height="23" onClick="deal(form1)">
<input type="button" name="chongzhi" value="重置" width="61" height="23" onClick="form1.reset();">
</div>
</form>
(2)在index.jsp页面的合适位置添加用于显示进度条的<div>标记和显示百分比的<span>标记。具体代码如下:
<div id="progressBar" class="prog_border" align="left"><img src="images/progressBar.png" width="0" height="13" id="imgProgress"> </div> ?<span id="progressPercent" style="40px;display:none">0%</span>
(3)在CSS样式表文件style.css中,添加用于控制进度条样式的CSS样式。具体代码如下:
.prog_border{ height:15px; //高度 widht:255px; //宽度 background:#9ce0fd; //背景颜色 border:1px solid #FFFFFF; //边框样式 margin:0; padding:0; display:none; position:relative left:25px; float:left; //居左对齐 }
(4)在index.jsp页面的<head>标记中,编写自定义的JavaScript函数deal(),用于提交表单并设置每隔500毫秒获取一次上传进度。deal()函数的具体代码如下:
<script type="text/javascript"> function deal(form){ form.submit(); //提交表单 timer=window.setInterval("getProgress()", 500); //每隔500毫秒获取一次上传进度 }
</script>
(5)编写上传文件的Servlet实现类UpLoad。在该Servlet中编写实现文件上传的方法uploadFile()。在uploadFile()方法中,将调用Common-FileUpload组件分段上传文件,并计算上传百分比,将其实时保存到Session中。
Apache commons-fileupload 的使用:
1) 去 http://commons.apache.org/fileupload/ 下载fileupload jar包
同时下载 commons-fileupload 和 commons-io 两个包 -------- 因为fileupload依赖io包
2) 将jar包导入 web 工程WEB-INF/lib下
uploadFile()方法的具体代码如下:
package com.cn.Ajax; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UpLoad extends HttpServlet { public void uploadFile(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=GBK"); request.setCharacterEncoding("GBK"); HttpSession session=request.getSession(); session.setAttribute("progressBar", 0); //定义上传进度的Session变量 String error = ""; int maxSize = 50*1024*1024; //单个文件上传大小的上限 DiskFileItemFactory factory = new DiskFileItemFactory(); //基于磁盘文件项目创建一个工厂对象 ServletFileUpload upload = new ServletFileUpload(factory); //创建一个新的文件上传对象 try { List items = upload.parseRequest(request); //解析上传请求 Iterator itr = items.iterator(); //枚举方法 while(itr.hasNext()){ FileItem item =(FileItem) itr.next(); //获取FileItem对象 if(!item.isFormField()){ //判断是否为文件域 if(item.getName()!=null&&!item.getName().equals("")){ //判断是否选择了文件 long upFileSize = item.getSize(); //上传文件的大小 String fileName = item.getName(); //获取文件名 if(upFileSize>maxSize){ error="您上传的文件太大,请选择不超过50MB的文件"; break; } //此时文件暂存在服务器的内存中 File tempFile = new File(fileName); //构造临时对象 //获取根目录对应的真实物理路径 File file = new File(request.getRealPath("/upload"),tempFile.getName()); InputStream is = item.getInputStream(); int buffer=1024; int length =0; byte[] b=new byte[buffer]; double percent=0; FileOutputStream fos = new FileOutputStream(file); while((length=is.read(b))!=-1){ percent += length/(double)upFileSize*100D; //计算上传文件的百分比 fos.write(b,0,length); //向文件输出流写读取的数据 session.setAttribute("progressBar", Math.round(percent)); //将上传百分比保存到Session中 } fos.close(); Thread.sleep(1000); //线程休眠1秒 }else { error="没有选择上传文件!"; } } } } catch (Exception e) { e.printStackTrace(); error = "上传文件出现错误:"+e.getMessage(); } if(!"".equals(error)){ request.setAttribute("error", error); request.getRequestDispatcher("error.jsp").forward(request, response); }else { request.setAttribute("result", "文件上传成功!"); request.getRequestDispatcher("upFile_deal.jsp").forward(request, response); } } }
(6)由于要使用Ajax,所以需要创建一个封装Ajax必须实现功能的对象AjaxRequest,并将其代码保存为AjaxRequest.js,
AjaxRequest.js
var net = new Object(); //定义一个全局变量net //编写构造函数 net.AjaxRequest=function(url,onload,onerror,method,params){ this.req = null; this.onload = onload; this.onerror=(onerror)?onerror:this.defaultError; this.loadDate(url,method,params); } //编写用于初始化XMLHttpRequest对象并指定处理函数,最后发送HTTP请求的方法 net.AjaxRequest.prototype.loadDate=function(url,method,params){ if(!method){ method="GET"; } if(window.XMLHttpRequest){ this.req=new XMLHttpRequest(); }else if(window.ActiveXObject){ this.req = new ActiveXObject("Microsoft.XMLHTTP"); } if(this.req){ try{ var loader = this; this.req.onreadystatechange=function(){ net.AjaxRequest.onReadyState.call(loader); } this.req.open(method,url,true); //建立对服务器的调用 if(method=="POST"){ this.req.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //设置请求头 } this.req.send(params); }catch(err){ this.onerror.call(this); } } } //重构回调函数 net.AjaxRequest.onReadyState = function(){ var req=this.req; var ready=req.readyState; if(ready==4){ if(req.status==200){ this.onload.call(this); }else{ this.onerror.call(this); } } } //重构默认的错误处理函数 net.AjaxRequest.prototype.defaultError = function(){ alert("错误数据 回调状态:"+this.req.readyState+" 状态:"+this.req.status); }
然后在index.jsp页面中通过以下代码包含该文件:
<script language="javascript" src="JS/AjaxRequest.js"></script>
说明:通常情况下,在处理POST请求时,需要将请求头设置为application/x-www-form-urlencoded。但是,如果将表单的enctype属性设置为multipart/form-data,在处理请求时,就需要将请求头设置为multipart/form-data。
(7)在index.jsp页面中,编写自定义的JavaScript函数getProgress(),用于实例化Ajax对象。getProgress()函数的具体代码如下:
function getProgress(){ var loader=new net.AjaxRequest("showProgress.jsp?nocache="+new Date().getTime(), deal_p, onerror, "GET"); }
在上面的代码中一定要加代码“?nocache="+new Date().getTime()”,否则将出现进度不更新的情况。
(8)编写showProgress.jsp页面,在该页面中只需要应用EL表达式输出保存上传进度的Session变量。具体代码如下:
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> ${progressBar}
(9)编写Ajax的回调函数deal_p(),用于显示上传进度条及完成的百分比。deal_p()函数的具体代码如下:
function deal_p(){ var h=this.req.responseText; h=h.replace(/s/g,""); //去除字符串中的Unicode空白符 document.getElementById("progressPercent").style.display="";//现实百分比 progressPercent.innerHTML=h+"%"; //显示完成的百分比 document.getElementById("progressBar").style.display="block"; //显示进度条 document.getElementById("imgProgress").width=h*(255/100); //显示完成的进度 }
(10)编写Ajax的错误处理函数onerror(),在该函数中,添加弹出“出错了”提示对话框的代码。onerror()函数的具体代码如下:
function onerror(){ alert("上传文件出错!"); }
(11)编写error.jsp和upFile_deal.jsp代码如下:
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> <!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=GB18030"> <title>Insert title here</title> </head> <body> ${error}<br> </body> </html>
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> <!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=GB18030"> <title>Insert title here</title> </head> <body> ${result}<br> </body> </html>
完整的index.jsp页面代码如下:
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> <!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=GB18030"> <title>Insert title here</title> <script type="text/javascript"> function deal(form){ form.submit(); //提交表单 timer=window.setInterval("getProgress()", 500); //每隔500毫秒获取一次上传进度 } function getProgress(){ var loader=new net.AjaxRequest("showProgress.jsp?nocache="+new Date().getTime(), deal_p, onerror, "GET"); } function deal_p(){ var h=this.req.responseText; h=h.replace(/s/g,""); //去除字符串中的Unicode空白符 document.getElementById("progressPercent").style.display="";//现实百分比 progressPercent.innerHTML=h+"%"; //显示完成的百分比 document.getElementById("progressBar").style.display="block"; //显示进度条 document.getElementById("imgProgress").width=h*(255/100); //显示完成的进度 } function onerror(){ alert("上传文件出错!"); } </script> </head> <body> <form name="form1" enctype="multipart/form-data" method="post" action="UpLoad"> <div align="center"> 请选择要上传的文件:<br> <input type="file" name="file" size="42"><br> 注:文件大小请控制在50M以内。 </div> <table align="center"> <tr> <td align="left"> <div id="progressBar" class="prog_border" align="left"><img src="images/progressBar.png" width="0" height="13" id="imgProgress"></div> </td> <td> <span id="progressPercent" style="40px;display:none">0%</span> </td> </tr> </table> <div align="center"> <input type="button" name="shangchuan" value="上传" width="61" height="23" onClick="deal(form1)"> <input type="button" name="chongzhi" value="重置" width="61" height="23" onClick="form1.reset();"> </div> </form> <script language="javascript" src="JS/AjaxRequest.js"></script> </body> </html>
运行结果如下图所示: