提交表单信息
Web程序的任务是实现服务器与客户端浏览器之间的信息交互。客户端提交的信息可能来自表单里的文本框,密码框,选择框,单选按钮,复选框以及文件域。这些表单信息被以参数形式提交到了服务器。Servlet的任务就是正确地获取这些信息,并根据信息做出不同的响应。
提交信息的方式包括GET与POST,分别触发Servlet的doGet方法与doPost方法,一般而言,就像当初HHTP协议所设计的那样,GET用于从服务器获取信息(通过提交的参数指定要获取什么样的信息),而POST用于向服务器提交信息。POST方式提交数据又包括两种形式,即普通POST提交方式与可以上传文件的POST提交方式。
GET实现搜索引擎
HTML中使用FORM提交数据,当FORM的method属性设为GET时浏览器就以GET的方式提交表单数据。FORM的action属性设置数据将提交到哪个URL。
以GET方式提交数据时,浏览器把表单内容组织成一个查询字符串(Query String),各变量之间以“&"链接,然后以Servlet路径加问号"?"加查询字符串的形式获取服务器内容。
例如,要向某个Servlet(网址为http://servername/someServlet)提交两个参数a与b,a参数的值为aValue,b参数的值为bValue,则组织后的URL为http://servername/someServlet?a=aValue&b=bValue,Servlet中HttpServletRequest对象通过方法getParameter("a")可以获取到aValue,getParameter("b")获取到bValue,getQueryString()获取到字符串"a=aValue&b=bValue"(必要的时候要先使用setCharacterEncoding(encoding)设置实际编码方式)。GET方式提交表单内容时所有被提交的内容都将显示在浏览器地址栏中。
不经过FORM提交数据而直接以输入网址,或者单击链接的方式访问Servlet也被看做是GET方式提交数据。
GET方式提交表单的典型应用是搜索引擎。GET方式就是被设计为查询用的。下面使用GET方式制作一个搜索引擎。先新建一个简单的主界面search.html。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>search.html</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <style>div, body, span {font-size:14px; }</style> </head> <body> <div align="center"> <img src="images/yahoo.gif" style='margin:25px; '> <div> <form action='/servlet/servlet/SearchServlet' method='get'> <input type="radio" name="type" value="web" checked>网页 <input type="radio" name="type" value="news">新闻 <input type="radio" name="type" value="image">图片 <input type="radio" name="type" value="video">视频 <input type="checkbox" name="allowedAdult" value="true">允许成人内容 <br/><br/> <input type="text" name="word" value="" style="width:300px; "> <input type="submit" value="用雅虎搜索" style="width:100px; "> </form> </div> <div style="margin-top:50px; "> © Helloween 2007-2010 </div> </div> </body> </html>这是一个静态的HTML文本文件,它的内容不会随Request的不同而发生改变。该HTML文件的布局使用了标签<div>而没有使用<table>等。这是因为<div>要比<table>灵活的多。当<div>连同css样式表与JavaScript脚本语言或者Ajax一块儿使用时,<div>具有非常丰富的表现性。现在的Web 2.0站点都采用<div>标签来展示网页内容。
该HTML页面效果如图3.9所示。页面内包含一个<form>表单,单击查询后,将以GET方式访问<form>的action属性指定的Servlet。
然后写一个SearchServlet类来实现关键的业务逻辑部分:搜索引擎程序。这里使用Yahoo Search API来调用Yahoo搜索引擎数据库里的数据,读者需要到Yahoo下载API包,网址为http://developer.yahoo.com/search。也可以从随书光盘中找到yahoo_search-2.0.1.jar,将jar包加入到项目的libraries中,即/WEB-INF/lib/下(没有请新建)。使用向导新建SearchServlet,部分代码如下:
package com.helloweenvsfei.servlet; import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.yahoo.search.ImageSearchRequest; import com.yahoo.search.ImageSearchResult; import com.yahoo.search.ImageSearchResults; import com.yahoo.search.NewsSearchRequest; import com.yahoo.search.NewsSearchResult; import com.yahoo.search.NewsSearchResults; import com.yahoo.search.SearchClient; import com.yahoo.search.VideoSearchRequest; import com.yahoo.search.VideoSearchResult; import com.yahoo.search.VideoSearchResults; import com.yahoo.search.WebSearchRequest; import com.yahoo.search.WebSearchResult; import com.yahoo.search.WebSearchResults; public class SearchServlet extends HttpServlet { private static final long serialVersionUID = -1249623428210967632L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); request.setCharacterEncoding("UTF-8"); // 搜索关键字 String word = request.getParameter("word"); // 搜索类型 String type = request.getParameter("type"); // 是否允许成人内容。如果选中,则为 "true",否则为 null. String allowedAdult = request.getParameter("allowedAdult"); boolean adultOk = "true".equals(allowedAdult); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>" + word + " 搜索结果</TITLE></HEAD>"); out.println("<style>"); out.println(" body, div {font-size:12px; padding:2px; margin:0px; }"); out.println(" .imgDiv{float:left; width: 172px; height:250px; margin:2px; padding:2px; border:1px pink solid; overflow:hidden; }"); out.println("</style>"); out.println(" <BODY>"); out.println("<div style='float:left; height:40px; '><img src='../images/yahoo.gif'></div>"); out.println("<form action='" + request.getRequestURI() + "' method='get'>"); out.println(" <div style='height:40px; '>"); out.println(" <input type='radio' name='type' value='web' " + (type.equals("web")?"checked":"") + ">网页"); out.println(" <input type='radio' name='type' value='news' " + (type.equals("news")?"checked":"") + ">新闻"); out.println(" <input type='radio' name='type' value='image' " + (type.equals("image")?"checked":"") + ">图像"); out.println(" <input type='radio' name='type' value='video' " + (type.equals("video")?"checked":"") + ">视频"); out.println(" "); out.println(" <input type='checkbox' name='allowedAdult' value='true' " + (adultOk?"checked":"") + ">允许成人内容 <br/>"); out.println(" <input type='text' name='word' value='" + word + "' style='width:300px; '> <input type='submit' value='用雅虎搜索' style='width:100px; '>"); out.println(" </div>"); out.println("</form>"); SearchClient client = new SearchClient("javasdktest"); try{ if("image".equals(type)){ ImageSearchRequest searchRequest = new ImageSearchRequest(URLEncoder.encode(word, "UTF-8")); // 是否显示成人内容 searchRequest.setAdultOk(adultOk); // 查询记录数 searchRequest.setResults(20); // 从第 0 条记录开始显示 searchRequest.setStart(BigInteger.valueOf(0)); double startTime = System.currentTimeMillis(); ImageSearchResults results = client.imageSearch(searchRequest); double endTime = System.currentTimeMillis(); out.println("<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>"); out.println(" 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。"); out.println("</div>"); for(ImageSearchResult result : results.listResults()){ out.println("<div class='imgDiv'>"); out.println(" <div align='center'><a href=\"" + result.getClickUrl() + "\" target=_blank><img width=160 height=120 src=\"" + result.getThumbnail().getUrl() + "\" border='0'></a></div>"); out.println(" <div align='center'><a href=\"" + result.getRefererUrl() + "\" target=_blank>" + result.getTitle() + "</a></div>"); out.println(" <div align='center'>" + result.getWidth() + "x" + result.getHeight() + " " + result.getFileFormat() + "</div>"); out.println(" <div>" + (result.getSummary()==null ? "" : result.getSummary()) + "</div>"); out.println("</div>"); } } else if("web".equals(type)){ WebSearchRequest searchRequest = new WebSearchRequest(URLEncoder.encode(word, "UTF-8")); // 是否显示成人内容 searchRequest.setAdultOk(adultOk); // 查询记录数 searchRequest.setResults(20); // 从第 0 条记录开始显示 searchRequest.setStart(BigInteger.valueOf(0)); double startTime = System.currentTimeMillis(); WebSearchResults results = client.webSearch(searchRequest); double endTime = System.currentTimeMillis(); out.println("<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>"); out.println(" 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。"); out.println("</div>"); for(WebSearchResult result : results.listResults()){ out.println("<div style='margin:8px; width:500px; '>"); out.println(" <div><a href=\"" + result.getClickUrl() + "\" target=_blank><b>" + result.getTitle() + "</b></a> 文件格式:" + result.getMimeType() + "</div>"); out.println(" <div>网址:<a href=\"" + result.getUrl() + "\" target=_blank>" + result.getUrl() + "</a></div>"); out.println(" <div>" + result.getSummary() + (result.getCache()==null ? "" : " [<a href=\"" + result.getCache().getUrl() + "\" target=_blank>网页快照</a>]") +"</div>"); out.println("</div>"); } } else if("news".equals(type)){ NewsSearchRequest searchRequest = new NewsSearchRequest(URLEncoder.encode(word, "UTF-8")); // 是否显示成人内容 // searchRequest.setAdultOk(adultOk); // 查询记录数 searchRequest.setResults(20); // 从第 0 条记录开始显示 searchRequest.setStart(BigInteger.valueOf(0)); double startTime = System.currentTimeMillis(); NewsSearchResults results = client.newsSearch(searchRequest); double endTime = System.currentTimeMillis(); out.println("<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>"); out.println(" 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。"); out.println("</div>"); for(NewsSearchResult result : results.listResults()){ out.println("<div style='margin:8px; width:500px; '>"); out.println(" <div><a href=\"" + result.getClickUrl() + "\" target=_blank><b>" + result.getTitle() + "</b></a></div>"); out.println(" <div>网址:<a href=\"" + result.getUrl() + "\" target=_blank>" + result.getUrl() + "</a></div>"); out.println(" <div>" + result.getSummary() + "</div>"); out.println("</div>"); } } else if("video".equals(type)){ VideoSearchRequest searchRequest = new VideoSearchRequest(URLEncoder.encode(word, "UTF-8")); // 是否显示成人内容 searchRequest.setAdultOk(adultOk); // 查询记录数 searchRequest.setResults(20); // 从第 0 条记录开始显示 searchRequest.setStart(BigInteger.valueOf(0)); double startTime = System.currentTimeMillis(); VideoSearchResults results = client.videoSearch(searchRequest); double endTime = System.currentTimeMillis(); out.println("<div align=right style='width:100%; background: #FFDDDD; height:25px; padding:2px; border-top:1px solid #FF9999; margin-bottom:5px; '>"); out.println(" 总共 " + results.getTotalResultsAvailable() + " 条数据,总用时 " + ( endTime - startTime )/1000 + " 秒。"); out.println("</div>"); for(VideoSearchResult result : results.listResults()){ out.println("<div class='imgDiv'>"); out.println(" <div align='center'><a href=\"" + result.getClickUrl() + "\" target=_blank><img width=160 height=120 src=\"" + result.getThumbnail().getUrl() + "\" border='0'></a></div>"); out.println(" <div align='center'><a href=\"" + result.getRefererUrl() + "\" target=_blank>" + result.getTitle() + "</a></div>"); out.println(" <div align='center'>" + result.getWidth() + "x" + result.getHeight() + " " + result.getFileFormat() + "</div>"); out.println(" <div>" + (result.getSummary()==null ? "" : result.getSummary()) + "</div>"); out.println("</div>"); } } }catch(Exception e){ e.printStackTrace(); out.println("<font color=red>Exception: " + e.getMessage() + "</font>"); } out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } }为节省篇幅,代码中只列出了图片搜索的相关代码,完整代码请参看随书光盘。用该搜索引擎搜索图像时会显示图像的缩略图、文件名称、原始图像大小、图像格式,以及图像文字描述等内容。使用该搜索引擎程序搜索图片titanic,效果如下:
留意一下地址栏,使用GET方式提交数据时所有被提交的信息都显示在了地址栏中。由于没有自己的数据库,因此SearchServlet使用了Yahoo的API。Yahoo Search API提供了搜索网页、新闻、图片、视频的程序接口,只需要寥寥几句话就可以完成搜索过程。Yahoo Search API的工作原理通过访问Yahoo的Web Service来获取搜索数据。本书后面的章节中还会讲到怎么构建自己的Web Service。读者可以自行验证一下,该搜索引擎的搜索结果与Yahoo搜索引擎(http://www.yahoo.com)的搜索结果完全一致。
另外,要想在该搜索引擎中搜索中文,需要修改TOMCAT目录下的/conf/server.xml里设定的默认的GET编设方式,否则会出现乱码。下面为server.xml中需要修改的部分。粗体部分为添加的代码。如果不指定UTF-8方式编码,TOMCAT将使用ISO-8859-1编码。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />GET方式提交表单时,所有被提交的内容都会被显示在浏览器地址栏中,并可能会被浏览器记录在缓存里,因此提交敏感信息(如密码、银行账号等)时不能使用GET方式。GET提交时URL总长度不同浏览器有不同的限制,因此提交过长的内容时也不能使用GET方式。
POST提交个人信息
由于GET方式提交表单具有上述的限制,因此需要使用POST提交表单。把HTML中FORM的method属性设置为POST,浏览器即以POST方式提交表单内容。
POST方式提交表单时,表单的内容不会显示在浏览器中,浏览器中只显示接受该表单数据的Servlet路径,因此POST可以提交一些敏感信息(如密码等)。POST方式提交表单时也不受内容长度的限制,理论上可以接受非常大的数据量。
同GET方式一样,Servlet可以通过HttpServletRequest对象的getParameter(String param)方法获取param对应的参数值。不同的是,由于POST方式不会使用"?"以及"&“符号来组织一个QueryString,因此POST时使用getQueryString()将返回null。
下面使用POST方式来提交一个用户信息表单。使用向导在项目servlet的WebRoot文件夹下新建HTML文件,取名postPersonInformation.html。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>提交用户信息</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <style> body, div, td, input {font-size:12px; margin:0px; } select {height:20px; width:300px; } .title {font-size: 16px; padding: 10px; margin:10px; width:80%; } .text {height:20px; width:300px; border:1px solid #AAAAAA; } .line {margin:2px; } .leftDiv {width:110px; float:left; height:22px; line-height:22px; font-weight:bold; } .rightDiv {height:22px; } .button { color:#fff; font-weight:bold; font-size: 11px; text-align:center; padding:.17em 0 .2em .17em; border-style:solid; border-width:1px; border-color:#9cf #159 #159 #9cf; background:#69c url(images/bg-btn-blue.gif) repeat-x; } </style> </head> <body> <form action="/servlet/servlet/PostServlet" method="POST"> <div align="center"> <br/> <fieldset style='width:90%'> <legend>填写用户信息</legend> <br/> <div class='line'> <div align="left" class='leftDiv'>请填写您的姓名:</div> <div align="left" class='rightDiv'> <input type="text" name="name" class="text" /> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请填写您的密码:</div> <div align="left" class='rightDiv'> <input type="password" name="password" class="text" /> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请再次输入密码:</div> <div align="left" class='rightDiv'> <input type="password" name="passwordConfirm" class="text" /> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请选择性别:</div> <div align="left" class='rightDiv'> <input type="radio" name="sex" value="男" id="sexMale"> <label for="sexMale">男</label> <input type="radio" name="sex" value="女" id="sexFemale"> <label for="sexFemale">女</label> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请输入年龄:</div> <div align="left" class='rightDiv'> <input type="text" name="age" class="text"> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请输入生日:</div> <div align="left" class='rightDiv'> <input type="text" name="birthday" class="text"> <br/>格式:"yyyy-mm-dd" </div> </div> <div class='line'> <div align="left" class='leftDiv'>请选择您的爱好</div> <div align="left" class='rightDiv'> <input type="checkbox" name="interesting" value="音乐影视" id="i1"> <label for="i1">音乐影视</label> <input type="checkbox" name="interesting" value="外出旅游" id="i2"> <label for="i2">外出旅游</label> <input type="checkbox" name="interesting" value="社交活动" id="i3"> <label for="i3">社交活动</label> </div> </div> <div class='line'> <div align="left" class='leftDiv'>请选择省市:</div> <div align="left" class='rightDiv'> <select name="area"> <option>---请选择省份---</option> <optgroup label="北京市"> <option value="北京市海淀区">海淀区</option> <option value="北京市朝阳区">朝阳区</option> <option value="北京市东城区">东城区</option> <option value="北京市西城区">西城区</option> </optgroup> <optgroup label="山东省"> <option value="山东省济南市">济南市</option> <option value="山东省青岛市">青岛市</option> <option value="山东省潍坊市">潍坊市</option> </optgroup> </select> </div> </div> <div class='line'> <div align="left" class='leftDiv'>自我描述:</div> <div align="left" class='rightDiv'> <textarea name="description" rows="8" style="width:300px; ">请填写其他资料... </textarea> </div> </div> <div class='line'> <div align="left" class='leftDiv'></div> <div align="left" class='rightDiv'> <br/><input type="submit" name="btn" value=" 提交信息 " class="button"><br/> </div> </div> </fieldset> </div> </form> </body> </html>该页面内使用了文本框text,密码框password,单选按钮radio,复选框checkbox,选择框select,文本域textarea等。这些HTML组件的值都可以提交给Servlet。如果同一个名称的参数有多个值(例如页面内兴趣爱好最多可以有三个值),则以字符串数组的形式提交给Servlet,Servlet可以通过HttpServletRequest的getPrarameters(String param)取得这个字符串数组。
下面编写一个Servlet来接收HTML页面提交的信息。使用向导新建PostServlet
package com.helloweenvsfei.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LifeCycleServlet extends HttpServlet { private static final long serialVersionUID = -7197419401412129310L; private static double startPoint = 0; @Override public void init() throws ServletException { this.log("执行 init() 方法 ... "); ServletConfig conf = this.getServletConfig(); startPoint = Double.parseDouble(conf.getInitParameter("startPoint")); } @Override protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException { this.log("执行 service() 方法 ... "); super.service(arg0, arg1); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.log("执行 doPost() 方法 ... "); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML><HEAD><TITLE>个人所得税计算</TITLE></HEAD>"); out.println("<link rel='stylesheet' type='text/css' href='../css/style.css'>"); out.println("<BODY>"); out.println("<div align='center'><br/><fieldset style=width:90%><legend>个税计算器</legend><br/>"); try{ // 从参数中获取的工资数目 double income = new Double(request.getParameter("income")); // 应纳税部分 double charge = income - startPoint; // 缴税 double tax = 0; if (charge<=0) {tax=0;} if (charge>0&&charge<=500) {tax=charge*0.05;} if (charge>500&&charge<=2000) {tax=charge*0.1-25;} if (charge>2000&&charge<=5000) {tax=charge*0.15-125;} if (charge>5000&&charge<=20000) {tax=charge*0.2-375;} if (charge>20000&&charge<=40000) {tax=charge*0.25-1375;} if (charge>40000&&charge<=60000) {tax=charge*0.30-3375;} if (charge>60000&&charge<=80000) {tax=charge*0.35-6375;} if (charge>80000&&charge<=100000) {tax=charge*0.4-10375;} if (charge>100000) {tax=charge*0.45-15375;} out.println("<div style='line'>"); out.println(" <div class='leftDiv'>您的工资为</div><div class='rightDiv'>" + income + " 元</div>"); out.println("</div>"); out.println("<div style='line'>"); out.println(" <div class='leftDiv'>您应纳税</div><div class='rightDiv'>" + tax + " 元</div>"); out.println("</div><br/>"); out.println("<input type='button' οnclick='history.go(-1);' value='纳税光荣 逃税可耻 返回' class=button>"); }catch(Exception e){ out.println("请输入数值类型数据。<input type='button' οnclick='history.go(-1);' value='返回' class=button>"); } out.println("</BODY>"); out.println("</HTML>"); out.flush(); out.close(); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.log("执行 doGet() 方法 ... "); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<link rel='stylesheet' type='text/css' href='../css/style.css'>"); out.println("<HTML><HEAD><TITLE>个人所得税计算</TITLE></HEAD>"); out.println("<div align='center'><br/><fieldset style=width:90%><legend>个税计算器</legend><br/>"); out.println("<form method='post' action='LifeCycleServlet'>"); out.println("<div style='line'>"); out.println(" <div class='leftDiv'>您的工资为</div><div align='left' class='rightDiv'><input type='text' name='income'> 单位:元</div>"); out.println("</div><br/>"); out.println("<div style='line'>"); out.println(" <div class='leftDiv'></div><div align='left' class='rightDiv'><input type='submit' value=' 计算个税 ' class=button></div>"); out.println("</div>"); out.println("</form>"); out.println("<BODY>"); out.println("</BODY>"); out.println("</HTML>"); out.flush(); out.close(); } @Override public void destroy() { this.log("执行 destroy() 方法 ... "); startPoint = 0; } }程序运行效果如下:
可以看到以POST方式提交数据的时候,浏览器地址栏中不显示被提交的内容,提交的数据量也要大于GET。POST就是设计为提交数据的。
当提交的数据长度大于256个字符,或者要提交文件时,只能选择POST方式。
上传文件客户端
除了提交表单,上传文件也是很常见的客户端与Web程序交互的操作。电子相册,网络硬盘,邮件附件,视频网站等都是采用Web文件上传的形式。相对于FTP文件上传,Web文件上传速度要慢一些,但是使用方便,不需要客户端,仅有一个浏览器就可以,而且权限也比FTP容易控制。
Web文件上传也是采用POST的方式。与前面讲的POST提交表单不同的是,上传文件需要设置FORM的enctype属性为multipart/form-data。由于上传的文件会比较大,因此需要设置该参数指定浏览器使用二进制上传。如果不设置,enctype属性默认为application/x-www-form-urlencoded,浏览器将使用ASCII向服务器发送数据,导致发送文件失败。
上传文件要使用文件域(<input type="file"/>),并把FORM的Enctype设置为multipart/form-data,例如:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>上传文件</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" type="text/css" href="css/style.css"> </head> <body> <form action="servlet/UploadServlet" method="post" enctype="multipart/form-data"> <div align="center"><br/> <fieldset style="width:90%"> <legend>上传文件</legend><br/> <div class='line'> <div align='left' class="leftDiv">上传文件一</div> <div align='left' class="rightDiv"> <input type="file" name="file1" class="text"> </div> </div> <div class='line'> <div align='left' class="leftDiv">上传文件二</div> <div align='left' class="rightDiv"> <input type="file" name="file2" class="text"> </div> </div> <div class='line'> <div align='left' class="leftDiv">上传文件说明一</div> <div align='left' class="rightDiv"><input type="text" name="description1" class="text"></div> </div> <div class='line'> <div align='left' class="leftDiv">上传文件说明二</div> <div align='left' class="rightDiv"><input type="text" name="description2" class="text"></div> </div> <div class='line'> <div align='left' class="leftDiv"></div> <div align='left' class="rightDiv"><br/> <input type="submit" value=" 上传文件 " class="button"> </div> </div> </fieldset> </div> </form> </body> </html>上传文件服务器端
客户端运行的代码很简单,服务器要复杂一些。由于上传文件时浏览器是以二进制的方式发送数据,因此Servlet里不能简单地通过HttpServletRequest的getParameter()方法来获取文件域以及文本域的内容。要想获取其中的内容,必须根据HTTP协议所规定的格式解析浏览器提交的Request。
解析二进制数据流比较麻烦。已经有许多类库已经完成了这项工作,例如SmartUpload与Apache Commons Fileupload。SmartUpload是一个商业类库,解析Request过程中数据存放在内存里,因此速度较快,但上传大文件时会发生内存溢出。Apache Commons Fileupload是一个免费的开源类库。一些框架比如Struts里集成 Apache Commons Fileupload类库来实现文件上传。
Commons Fileupload是Apache commons众多开源组件中的一员。从Apache网站(http://www.apache.org)或者随书光盘中找到Apache Commons Upload类库,并将commons-fileupload.jar加入到项目的libraries中即/WEB-INF/lib/下,新建UploadServlet,代码如下:
package com.helloweenvsfei.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.URLEncoder; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.DiskFileUpload; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 7523024737218332088L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); response.getWriter().println("请以 POST 方式上传文件"); } @SuppressWarnings("unchecked") public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { File file1 = null, file2 = null; String description1 = null, description2 = null; response.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <link rel='stylesheet' type='text/css' href='../css/style.css'>"); out.println(" <BODY>"); out.println("<div align=center><br/>"); out.println("<fieldset style='width:90%'><legend>上传文件</legend><br/>"); out.println(" <div class='line'>"); out.println(" <div align='left' class='leftDiv'>上传日志:</div>"); out.println(" <div align='left' class='rightDiv'>"); // 使用 DiskFileUpload 对象解析 request DiskFileUpload diskFileUpload = new DiskFileUpload(); try { // 将 解析的结果 放置在 List 中 List<FileItem> list = diskFileUpload.parseRequest(request); out.println("遍历所有的 FileItem ... <br/>"); // 遍历 list 中所有的 FileItem for(FileItem fileItem : list){ if(fileItem.isFormField()){ // 如果是 文本域 if("description1".equals(fileItem.getFieldName())){ // 如果该 FileItem 名称为 description1 out.println("遍历到 description1 ... <br/>"); description1 = new String(fileItem.getString().getBytes(), "UTF-8"); } if("description2".equals(fileItem.getFieldName())){ // 如果该 FileItem 名称为 description2 out.println("遍历到 description2 ... <br/>"); description2 = new String(fileItem.getString().getBytes(), "UTF-8"); } } else{ // 否则,为文件域 if("file1".equals(fileItem.getFieldName())){ // 客户端文件路径构建的 File 对象 File remoteFile = new File(new String(fileItem.getName().getBytes(), "UTF-8")); out.println("遍历到 file1 ... <br/>"); out.println("客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>"); // 服务器端文件,放在 upload 文件夹下 file1 = new File(this.getServletContext().getRealPath("attachment"), remoteFile.getName()); file1.getParentFile().mkdirs(); file1.createNewFile(); // 写文件,将 FileItem 的文件内容写到文件中 InputStream ins = fileItem.getInputStream(); OutputStream ous = new FileOutputStream(file1); try{ byte[] buffer = new byte[1024]; int len = 0; while((len=ins.read(buffer)) > -1) ous.write(buffer, 0, len); out.println("已保存文件" + file1.getAbsolutePath() + "<br/>"); }finally{ ous.close(); ins.close(); } } if("file2".equals(fileItem.getFieldName())){ // 客户端文件路径构建的 File 对象 File remoteFile = new File(new String(fileItem.getName().getBytes(), "UTF-8")); out.println("遍历到 file2 ... <br/>"); out.println("客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>"); // 服务器端文件,放在 upload 文件夹下 file2 = new File(this.getServletContext().getRealPath("attachment"), remoteFile.getName()); file2.getParentFile().mkdirs(); file2.createNewFile(); // 写文件,将 FileItem 的文件内容写到文件中 InputStream ins = fileItem.getInputStream(); OutputStream ous = new FileOutputStream(file2); try{ byte[] buffer = new byte[1024]; int len = 0; while((len=ins.read(buffer)) > -1) ous.write(buffer, 0, len); out.println("已保存文件" + file2.getAbsolutePath() + "<br/>"); }finally{ ous.close(); ins.close(); } } } } out.println("Request 解析完毕"); } catch (FileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } out.println(" </div>"); out.println(" </div>"); if(file1 != null){ out.println(" <div class='line'>"); out.println(" <div align='left' class='leftDiv'>file1:</div>"); out.println(" <div align='left' class='rightDiv'>"); out.println(" <a href='" + request.getContextPath() + "/attachment/" + file1.getName() + "' target=_blank>" + file1.getName() + "</a>" ); out.println(" </div>"); out.println(" </div>"); } if(file2 != null){ out.println(" <div class='line'>"); out.println(" <div align='left' class='leftDiv'>file2:</div>"); out.println(" <div align='left' class='rightDiv'>"); out.println(" <a href='" + request.getContextPath() + "/attachment/" + URLEncoder.encode(file2.getName(), "UTF-8") + "' target=_blank>" + file2.getName() + "</a>" ); out.println(" </div>"); out.println(" </div>"); } out.println(" <div class='line'>"); out.println(" <div align='left' class='leftDiv'>description1:</div>"); out.println(" <div align='left' class='rightDiv'>"); out.println(description1); out.println(" </div>"); out.println(" </div>"); out.println(" <div class='line'>"); out.println(" <div align='left' class='leftDiv'>description2:</div>"); out.println(" <div align='left' class='rightDiv'>"); out.println(description2); out.println(" </div>"); out.println(" </div>"); out.println("</fieldset></div>"); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } }UploadServlet中将上传过程日志打印在页面上。上传成功后显示上传文件的链接地址,可以直接在浏览器中打开查看上传后的文件效果。程序运行效果如下:
上传文件时数据将以二进制形式提交,而非ASCII方式提交,因此Servlet不能用request.getParameter()等方式获取提交的文本内容。本例使用Commons-upload解析二进制数据,获取上传的文本内容。
转载于:https://www.cnblogs.com/liunianfeiyu/p/10657612.html
相关资源:JSP & Servlet学习笔记