Servlet学习记录3

时间:2021-08-31 21:44:16

  提交表单信息

  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="data: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">视频 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<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; ">
&copy; 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(" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
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,效果如下:

Servlet学习记录3

  留意一下地址栏,使用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' onclick='history.go(-1);' value='纳税光荣 逃税可耻 返回' class=button>"); }catch(Exception e){
out.println("请输入数值类型数据。<input type='button' onclick='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;
} }

  程序运行效果如下:

Servlet学习记录3

  可以看到以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中将上传过程日志打印在页面上。上传成功后显示上传文件的链接地址,可以直接在浏览器中打开查看上传后的文件效果。程序运行效果如下:

Servlet学习记录3

  上传文件时数据将以二进制形式提交,而非ASCII方式提交,因此Servlet不能用request.getParameter()等方式获取提交的文本内容。本例使用Commons-upload解析二进制数据,获取上传的文本内容。