2.2 发送请求 2.2.4 发送XML请求

对于请求参数为大量key-value对的情形,使用简单的POST请求比较合适。但对于某些极端的情形,如请求参数特别多,而且请求参数的结构关系复杂的情况,则可以考虑发送XML请求。**XML请求的实质还是POST请求,只是在发送请求的客户端页面将请求参数封装成XML字符串的形式**,服务器端则负责解析该XML字符串。当然,服务器获取XML字符串后,可借助于dom4jJDOM等工具来解析。

发送的XML请求实际上依然是POST请求,只是请求参数不再以param=value的形式被发送,而是直接采用XML字符串作为参数。这意味着服务器端不能直接获取请求参数,而是必须以流的形式获取请求参数.

实例

下面对前述的级联菜单应用进行简单修改,修改后的级联菜单允许一次选取多个国家。如果一次选取了多个国家,则服务器返回多个国家对应的城市—请求参数采用XML文档发送。
first.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>发送XML请求</title>
<style type="text/css">
select {
width: 120px;
font-size: 11pt;
}
</style>
</head>
<body>
<!-- 支持多选的列表框 -->
<select name="first" id="first" size="5" multiple="multiple">
<option value="1" selected="selected">中国</option>
<option value="2">美国</option>
<option value="3">日本</option>
</select>
<!-- 用于发送Ajax请求的按钮 -->
<input type="button" value="发送" onClick="send();" />
<!-- 被级联改变的列表框 -->
<select name="second" id="second" size="5">
</select>
<script type="text/javascript">
String.prototype.trim = function() {
//移除首尾的空格
return this.replace(/^\s+/, "").replace(/\s+$/, "");
}
// 定义创建XML文档的函数
function createXML() {
// 开始创建XML文档,countrys是根元素
var xml = "<countrys>";
// 获取first元素的,并获取起所有的子节点(选项)
var options = document.getElementById("first").childNodes;
var option = null;
// 遍历国家下拉列表的所有选项
for (var i = 0; i < options.length; i++) {
option = options[i];
// 如果某个选项被选中
if (option.selected) {
// 在countrys的根节点下增加一个country的子节点
xml = xml + "<country>" + option.value
+ "<\/country>";
}
}
// 结束XML文档的根节点
// 在生成XML字符串时,为了避免系统对正斜杠(/)进行特殊处理,
// 使用了转义,即在正斜杠前增加反斜杠(\)。
xml = xml + "<\/countrys>";
// 返回XML文档
return xml;
}
// 1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 定义发送XML请求的函数
function send() {
// 2.定义请求发送的URL
var uri = "second.jsp";
// 3.打开与服务器连接
xhr.open("POST", uri, true);
// 4.设置请求头
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
// 5.指定当XMLHttpRequest对象状态发生改变时触发processResponse函数
xhr.onload = processResponse;
// 6.发送XML请求
xhr.send(createXML());
}
// 定义处理服务器响应的回调函数
function processResponse() {
if (xhr.status == 200) {
// 获取服务器响应字符串,并以$作为分隔符分割成多个字符串
var cityList = xhr.responseText.split("$");
var displaySelect = document
.getElementById("second");
// 清空second下拉列表的选项
displaySelect.innerHTML = "";
for (var i = 0; i < cityList.length; i++) {
if (cityList[i].trim().length > 0) {
// 依次创建多个option元素
option = document.createElement("option");
option.innerHTML = cityList[i];
// 将创建的option元素添加到下拉列表最后
displaySelect.appendChild(option);
}
}
}
}
</script>
</body>
</html>

second.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<%@ page contentType="text/html; charset=utf-8" language="java"%>
<%@ page
import="java.io.*,org.dom4j.*,org.dom4j.io.XPPReader,java.util.*"%>
<%
// 定义一个StringBuffer对象,用于接收请求参数
StringBuffer xmlBuffer = new StringBuffer();
String line = null;
// 通过request对象获取输入流
BufferedReader reader = request.getReader();
// 依次读取请求输入流的数据
while ((line = reader.readLine()) != null)
{
xmlBuffer.append(line);
}
// 将从输入流中读取到的内容转换为字符串
String xml = xmlBuffer.toString();
// 以Dom4J开始解析XML字串串
Document xmlDoc = new XPPReader()
.read(new ByteArrayInputStream(xml.getBytes()));
// 获得countrys节点的所有子节点
List countryList = xmlDoc.getRootElement().elements();
// 定义服务器响应的结果
String result = "";
// 遍历countrys节点的所有子节点
for (Iterator it = countryList.iterator(); it.hasNext();)
{
Element country = (Element) it.next();
// 如果发送的该节点的值为1,表明选中了中国
if (country.getText().equals("1"))
{
result += "上海$广州$北京";
}
// 如果发送的该节点的值为1,表明选中了美国
else if (country.getText().equals("2"))
{
result += "$华盛顿$纽约$加洲";
}
// 如果发送的该节点的值为1,表明选中了日本
else if (country.getText().equals("3"))
{
result += "$东京$大板$福冈";
}
}
// 向客户端发送响应
out.println(result);
%>

2.2 发送请求 2.2.3 发送POST请求

发送步骤

发送POST请求通常需要执行如下三个步骤:

  • 1.使用open方法打开连接时,指定使用POST方式发送请求。
  • 2.设置正确的请求头,对于POST请求通常应设置Content-Type请求头。
  • 3.发送请求,把请求参数转换为查询字符串(就是key=value&key=value...这种形式),将该字符串作为send()方法的参数

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 事件处理函数,当下拉列表选择改变时,触发该事件
function change(id) {
// 2.设置请求响应的URL
var uri = "second.jsp";
// 2.打开与服务器响应地址的连接
xhr.open("POST", uri, true);
// 3.设置处理响应的回调函数
xhr.onload = processResponse;
// 4.设置POST请求的请求头
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
// 5.发送请求,请求参数设为为查询字符串(也就是这种形式:key=value&...)
xhr.send("id=" + id);
}

事实上,即使采用POST方式发送请求,一样可以将请求参数附加在请求的URL之后。将change函数改为如下形式也可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 事件处理函数,当下拉列表选择改变时,触发该事件
function change(id) {
// 2.post请求也可以把请求参数放到URL的查询字符串中
var uri = "second.jsp?id="+id;
// 2.打开与服务器响应地址的连接
xhr.open("POST", uri, true);
// 3.设置处理响应的回调函数
xhr.onload = processResponse;
// 4.设置POST请求的请求头
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
// 5.发送请求,请求参数设为为查询字符串(也就是这种形式:key=value&...)
xhr.send();
}

完整代码

项目结构

1
2
3
4
5
6
7
8
E:\workspace_web2\post
└─WebContent
├─first.html
├─META-INF
│ └─MANIFEST.MF
├─second.jsp
└─WEB-INF
└─lib

这是一个java web项目把这下两个文件放在Java web项目的根目录下即可运行.
first.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>发送GET请求</title>
<style type="text/css">
select {
width: 160px;
font-size: 11pt;
}
</style>
</head>
<body>
<!-- 一级菜单 -->
<select name="first" id="first" size="4"
onchange="change(this.value);">
<option value="1" selected="selected">中国</option>
<option value="2">美国</option>
<option value="3">日本</option>
</select>
<!-- 二级菜单 -->
<select name="second" id="second" size="4">
</select>
<script type="text/javascript">
// 1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// // 事件处理函数,当下拉列表选择改变时,触发该事件
// function change(id) {
// // 2.设置请求响应的URL
// var uri = "second.jsp";
// // 2.打开与服务器响应地址的连接
// xhr.open("POST", uri, true);
// // 3.设置处理响应的回调函数
// xhr.onload = processResponse;
// // 4.设置POST请求的请求头
// xhr.setRequestHeader("Content-Type",
// "application/x-www-form-urlencoded");
// // 5.发送请求,请求参数设为为查询字符串(也就是这种形式:key=value&...)
// xhr.send("id=" + id);
// }
// 事件处理函数,当下拉列表选择改变时,触发该事件
function change(id) {
// 2.post请求也可以把请求参数放到URL的查询字符串中
var uri = "second.jsp?id="+id;
// 2.打开与服务器响应地址的连接
xhr.open("POST", uri, true);
// 3.设置处理响应的回调函数
xhr.onload = processResponse;
// 4.设置POST请求的请求头
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
// 5.发送请求,请求参数设为为查询字符串(也就是这种形式:key=value&...)
xhr.send();
}
// 定义处理响应的回调函数
function processResponse() {
//如果响应成功
if (xhr.status == 200) {
// 将服务器响应以$符号分隔成字符串数组
var cityList = xhr.responseText.split("$");
// 获取用于显示菜单的下拉列表
var displaySelect = document
.getElementById("second");
// 将目标下拉列表清空
displaySelect.innerHTML = "";
// 以字符串数组的每个元素创建option,
// 并将这些选项添加到下拉列表中
for (var i = 0; i < cityList.length; i++) {
// 创建一个<option.../>元素
var op = document.createElement("option");
op.innerHTML = cityList[i];
// 将新的选项添加到列表框的最后
displaySelect.appendChild(op);
}
} else {
// 页面响应不正常
window.alert("您所请求的页面有异常。");
}
}
//页面加载完成时调用
document.body.onload = change(1);
</script>
</body>
</html>

second.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page contentType="text/html; charset=utf-8" language="java" %>
<%
String idStr = (String)request.getParameter("id");
int id = idStr == null ? 1 : Integer.parseInt(idStr);
System.out.println(id);
switch(id)
{
case 1:
%>
上海$广州$北京
<%
break;
case 2:
%>
华盛顿$纽约$加州
<%
break;
case 3:
%>
东京$大板$福冈
<%
break;
}
%>

2.2 发送请求 2.2.2发送GET请求

通常情况下

  • 通常情况下,GET 请求用于从服务器上获取数据,POST 请求用于向服务器发送数据
  • GET请求将所有请求参数转换成一个查询字符串,并将该字符串添加到请求的URL之后,因而可在请求的URL后看到请求参数名、请求参数值.
  • POST请求则将请求的参数及对应的值放在HTML Header中传输,用户看不到明码的请求参数值。
  • 如果将某个表单的action属性设置为GET,则请求会将表单中各字段的名和值转换成字符串,并附加到URL之后。
  • GET请求传送的数据量较小,一般不能大于2KBPOST传送的数据量较大,通常认为POST请求参数的大小不受限制,但往往取决于服务器的限制。通常来说,POST请求的数据量总比GET请求的数据量大。

使用Ajax

当使用Ajax发送异步请求时,建议使用POST请求,而不是GET请求.
Ajax使Get请求时,如需要发送请求参数,要把请求参数放到URL的查询字符串上
按照XMLHttpRequest规范,使用send方法发送GET请求时,无须为send()方法传入任何参数;但早期有些浏览器可能必须传入null作为send方法的参数。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 事件处理函数,当下拉列表选择改变时,触发该事件
function change(id) {
// 2.设置请求响应的URL
// 请求参数放到url的查询字符串中
var uri = "second.jsp?id=" + id;
// 2.打开与服务器响应地址的连接
xhr.open("GET", uri, true);
// 3.设置处理响应的回调函数
xhr.onload = processResponse;
// 4.发送请求,不写参数
xhr.send();
}

最后,Ajax请求在后台发送,所以使用Ajax发送Get请求时,浏览器地址栏上的地址不会发生改变.

2.2 发送请求 2.2.1发送简单请求

传统Web应用采用表单或请求某个资源的方式发送请求;而Ajax则采用异步方式在后台发送请求

所谓简单请求,是指不包含任何参数的请求。这种请求通常用于自动刷新的应用,例如证券交易所的实时信息发送。这种请求通常用于公告性质的响应,公告性质的响应不需要客户端的任何请求参数,而是由服务器根据业务数据自动生成。

Ajax发送请求流程

对于简单请求,因为无须发送请求参数,所以采用POSTGET方式并没有太大区别。不管发送怎样的请求,XMLHttpRequest对象都应该按如下步骤来做:

  • 初始化,也是就是获取XMLHttpRequest对象.
  • 打开与服务器的连接(open方法)。打开连接时,
    • 指定发送请求的方法:是采用GET还是POST
    • 指定发送请求的服务器资源的地址
    • 指定是否采用异步方式,true为异步。
  • 设置监听XMLHttpRequest对象状态改变的事件处理函数。
  • 发送请求(send方法)。如采用POST方法发送请求,可发送带参数的请求。

open方法参数

open方法通常有三个参数:

  • 第一个参数指定发送请求的方式—只能是POSTGET,通常建议采用POST方式;
  • 第二个参数指定发送请求的服务器资源的地址;
  • 第三个参数只能为truefalse,用于指定是否采用异步方式发送请求。

2.1.3 XMLHttpRequest对象的事件

服务器响应完成时,load事件的处理函数将被触发

事件及其触发条件

  • progress:当XMLHttpRequest对象处于与服务器通信过程时触发注册了该事件的事件处理函数。
  • load:当XMLHttpRequest对象获取服务器响应完成时触发注册了该事件的事件处理函数。
  • error:当XMLHttpRequest对象与服务器通信出错时触发注册了该事件的事件处理函数。
  • abort:当XMLHttpReques对象取消与服务器通信时触发注册了该事件的事件处理函数。
  • readystatechange:当XMLHttpReques对象的**readyState属性发生改变时触发**注册了该事件的事件处理函数。

2.1.2 XMLHttpRequest对象的属性

XMLHttpRequest对象常用的属性如下:

readyState属性

readyState:该属性用于获取XMLHttpRequest对象的处理状态.

readyState属性值 描述
0 XMLHttpRequest对象还未开始发送请求
1 XMLHttpRequest对象开始发送请求
2 XMLHttpRequest对象的请求发送完成
3 XMLHttpRequest对象开始读取服务器的响应
4 XMLHttpRequest对象读取服务响应结束

响应相关属性

responseType属性

responseType:该属性用于设置服务器返回的响应类型

response属性

response:该属性用于获取服务器响应。不同responseType对应的response如表2.2所示。

表2.2 responseType与response
reponseType属性 response属性
"" response属性返回DOMString,默认
"arraybuffer" response属性返回ArrayBuffer二进制数据
"blob" response属性返回Blob对象
"document" response属性返回Document对象,与responseXML返回值相同
"json" response属性返回JavaScript对象,该对象通过解析服务器响应的JSON字符串得到
"text" response属性返回DOMString,默认
  • responseText:该属性用于获取服务器的响应文本。
  • responseXML:该属性用于获取服务器响应的XML文档对象。
  • responseURL:该属性返回服务器响应来自的URL

响应状态

  • status:该属性是服务器返回的状态码,只有服务器的响应已经完成时,才会有该状态码
  • statusText:该属性是服务器返回的状态文本信息,只有当服务器的响应已经完成时,才会有该状态文本信息。

服务器响应状态码

通过检测XMLHttpRequest对象的status属性,即可判断服务器的响应是否正常。当服务器的响应正常时,JavaScript才应该读取服务器响应信息,并将响应信息动态加载到目标页面。服务器常用的状码及其对应的含义如下。

  • 200:服务器响应正常
  • 304:该资源在上次请求之后没有任何修改。这通常用于浏览器的缓存机制,使用GET请求时尤其需要注意。
  • 400:无法找到请求的资源。
  • 401:访问资源的权限不够。
  • 403:没有权限访问资源
  • 404:需要访问的资源不存在
  • 405:需要访问的资源被禁止访问。
  • 407:访问资源需要代理身份验证。
  • 414:请求的URL太长。
  • 500:服务器内部错误

发送相关属性

  • timeout:设置XMLHttpRequest对象发送请求的超时时长,以ms为单位。
  • ontimeout:绑定事件处理函数的属性。该属性用于指定XMLHttpRequest对象发送请求超时后触发的事件处理函数。
  • upload:该属性返回XMLHttpRequest对象的上传进度。通过为该对象的progress事件注册事件处理函数,可以实时监听XMLHttpRequest对象的上传进度。

其他属性

  • withCredentials:该属性设置跨站点访问的请求是否应该使用安全凭证(如Cookie、授权头或TLS客户端证书等)。设置该属性对相同站点的请求不会有任何影响。

响应处理流程

如果想通过JavaScript获取服务器响应,只要为XMLHttpRequest对象的load事件注册事件处理函数即可。获取服务器响应之后,还应判断服务器响应是否正确,要判断服务器响应是否正确,可判断XMLHttpRequest对象的status属性。status值为200时,服务器响应正确,否则响应不正常

2.1 XMLHttpRequest对象的方法和属性

  • XmlHttpRequest对象的方法并不多,下面是其基本方法:
  • abort()。停止发送当前请求。
  • getAllResponseHeaders()。获取服务器返回的全部响应头。
  • getResponseHeader("headerLabel")。根据响应头的名字,获取对应的响应头。
  • open("method","URL"[,asyncFlag[,"userName"[,"password"]]])。建立与服务器URL的连接,并设置请求的方法,以及是否使用异步请求。如果远程服务需要用户名、密码,则提供对应的信息。
  • overrideMimeType(mimetype)。覆盖服务器所返回的数据的MIME类型。
  • send(content)。发送请求。其中content是请求参数。早期XMLHttpRequest对象只能发送字符串参数或XMLDocumentHTML5扩展了send()方法的功能,现在该方法还可发送表单数据、Blob对象、文件和ArrayBufferView对象。
  • setRequestHeader(″label″,″value″)。在发送请求之前,先设置请求头。

获取服务器响应头

在请求被发送之后,getAllResponseHeadersgetResponseHeader这两个方法,可用于获取服务器响应头。

2.0 HTML5增强的XMLHttpRequest对象

XMLHttpRequest对象可用于发送异步请求,也是Ajax技术的核心。异步发送请求是根本,不刷新页面动态加载只是表面现象。前端Ajax应用要使用XMLHttpRequest对象异步发送请求,既可以发送GET请求,也可以发送POST请求,也可以发送请求参数。

传统Web应用中发送请求不同,XMLHttpRequest对象必须以编程方式发送请求。
在请求发送出去之后,服务器响应会在合适的时候返回,但客户端浏览器不会自动加载这种异步响应,必须先调用XMLHttpRequest对象的responseTextresponseXMLresponse来获取服务器响应,
再通过DOM操作将服务器响应动态加载到当前页面中。

同步

每次用户发送请求,向服务器请求获得新数据时,浏览器都会完全丢弃当前页面,而等待重新加载新的页面。而在服务器完全响应之前,用户浏览器将一片空白,用户的动作必须中断

异步

异步是指用户发送请求后,无须等待,请求在后台发送,不会阻塞用户当前活动。用户无须等待第一次请求得到完全响应,即可发送第二次请求

Ajax应用的工作过程

  • JavaScript脚本使用XMLHttpRequest对象向服务器发送请求。发送请求时,既可以发送GET请求,也可以发送POST请求。
  • JavaScript脚本使用XMLHttpRequest对象解析服务器响应数据
  • JavaScript脚本通过DOM动态更新HTML页面。也可以为服务器响应数据增加CSS样式表,在当前网页的某个部分加以显示。

JavaScript 使用Ajax过程

JavaScript主要完成Ajax如下事情:

  • 1.创建XMLHttpRequest对象。
  • 2.通过XMLHttpRequest对象向服务器发送请求。
  • 3.创建回调函数,监视服务器响应状态,在服务器响应完成后,回调函数将会被调用。
  • 4.回调函数通过DOM动态更新HTML页面。

使用XMLHttpRequest对象发送请求步骤

一般而言,使用XMLHttpRequest对象发送请求应按如下步骤进行:

  • 1.使用open()方法连接服务器URL
  • 2.调用setRequestHeader()方法为请求设置合适的请求头。根据不同的请求,可能需要设置不同的请求头。
  • 3.为load事件注册事件处理函数。当服务器响应返回时,load事件处理函数将会被触发
  • 4.调用send()方法发送请求。

传统Web应用发送请求形式

  • 在浏览器的地址栏中输入请求地址后按回车键发送GET请求。
  • 提交表单发送POSTGET请求,具体发送何种请求取决于表单元素的method属性。

获取服务器响应生成的文本

XMLHttpRequest对象包含一个属性responseText,该属性可获取服务器响应被生成的文本

获取服务器响应的状态码

XMLHttpRequest对象提供了status属性,该属性是服务器响应对应的状态码,其中**200表明响应正常**,而404表明资源丢失,500表明内部错误等

传统Web编程和Ajax编程的区别

1.客户端发送请求的方式不同

传统Web应用发送请求通常有两种方式:采用提交表单的方式发送GET请求或POST请求;让浏览器直接请求网络资源发送GET请求。在采用Ajax的现代Web应用中,应用需要使用XMLHttpRequest对象来发送请求。

2.服务器生成的响应不同

在传统Web应用中,服务器的响应总是完整的HTML页面。在采用Ajax技术之后,服务器响应不再是完整的HTML页面,而只是必须更新的数据,因此服务器生成的响应可能只是简单的文本(当然也可以是JSON格式或XML格式的文本)。

3.客户端加载响应的方式不同

传统Web应用具有每个请求对应一个页面的特点,而且服务器响应就是一个完整的HTML页面,所以浏览器可以自动加载并显示服务器响应。在采用Ajax技术后,服务器响应只是必须更新的数据,故客户端必须通过程序来动态加载服务器响应

7.1 文件上传

文件上传是项目开发当中最常用的功能。为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这种情况下,浏览器才会把用户选择的文件二进制数据发送给服务器
一旦将enctype设置为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。2003年,Apache Software Foundation发布了开源的Commons File Upload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
Servlet3.0规范的HttpServletRequest已经提供了方法来处理文件上传,但这种上传需要在Servlet中完成。而Spring MVC则提供了更简单的封装。
Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。SpringMVC使用ApacheCommonsFileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons File Upload的组件
Apache Commons File Upload的组件共有两个,最新版本分别是commons-fileupload-1.3.3.jarcommons-io-2.6.jar
commons-fileupload-1.3.3.jar的官网下载地址为:http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi
commons-io-2.6.jar的官网下载地址为:http://commons.apache.org/proper/commons-io/download_io.cgi

示例 Spring MVC的文件上传

Spring配置文件中添加配置

Spring MVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。**如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver**。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 文件上传配置 -->
<bean
id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize">
<value>10485760</value>
</property>
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>

引入CommonsFileUpload的jar包

需要注意的是,CommonsMultipartResolver必须依赖于ApacheCommonsFileUpload的组件,所以需要将ApacheCommonsFileUploadjar包放到项目的类路径下。

MultipartFile对象

Spring MVC会将上传文件绑定到MultipartFile对象。MultipartFile提供了获取上传文件内容、文件名等方法。通过transferTo()方法可以将文件上传。MultipartFile对象中的常用方法如下:

方法 描述
byte[] getBytes() 获取文件数据
String getContentType() 获取文件MIME类型,如image/jpeg
InputStream getInputStream() 获取文件流
String getName() 获取表单中文件组件的名称
Stringg etOriginalFilename() 获取上传文件的原名
long getSize() 获取文件的字节大小,单位为byte
boolean isEmpty() 是否有上传的文件
void transferTo(Filedest) 将上传文件保存到一个目标文件中。

error.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试文件上传</title>
</head>
<body>
<strong>上传文件失败!</strong>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试文件上传</title>
</head>
<body>
<strong>上传文件成功!</strong>
</body>
</html>

uploadForm.jsp

负责上传文件的表单和一般表单有一些区别,**负责上传文件的表单的编码类型(enctype)必须是”multipart/form-data“**。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传</title>
</head>
<body>
<h2>文件上传</h2>
<form action="upload" enctype="multipart/form-data" method="post">
<table>
<tr>
<td>文件描述:</td>
<td><input type="text" name="description"></td>
</tr>
<tr>
<td>请选择文件:</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td><input type="submit" value="上传"></td>
</tr>
</table>
</form>
</body>
</html>

测试链接

1
<a href="uploadForm">Spring MVC的文件上传功能</a>

上传文件

输入文件描述信息并选择上传文件,如下图所示:
这里有一张图片
单击”上传“按钮,文件会被上传并保存到服务器部署项目的images文件夹下面。

上传效果

这里有一张图片

控制台输出

1
2
使用Spring MVC上传
上传文件路径:E:\apache-tomcat-8.5.35\webapps\FileUploadTest\images\2-2.jpg

进入该目录可以看到上传后的图片:
这里有一张图片

示例 使用对象接收上传文件

在实际的项目开发中,很多时候上传的文件会作为对象的属性被保存。Spring MVC的处理也非常简单。

域对象

上传的图片将作为User对象的属性,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 域对象,实现序列化接口
public class User
implements Serializable
{
private static final long serialVersionUID = 1L;
private String username;
// 对应上传的file,类型为MultipartFile,上传文件会自动绑定到image属性当中
private MultipartFile image;
public User()
{
super();
}
// 此处省略getter和setter方法,请自己补上
}

测试链接

1
<a href="registerForm">用户头像上传功能</a>

请求处理方法

1
2
3
4
5
6
@GetMapping(value = "/registerForm")
public String registerForm()
{
// 跳转到文件上传页面
return "registerForm";
}

registerForm.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="register" enctype="multipart/form-data" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>请上传头像:</td>
<td><input type="file" name="image"></td>
</tr>
<tr>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>

设置用户名和头像

这里有一张图片

注册请求处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@PostMapping(value = "/register")
// @ModelAttribute User user将整个表单数据绑定到User对象中
public String register(HttpServletRequest request, @ModelAttribute
User user, Model model) throws Exception
{
System.out.println(user.getUsername());
// 如果文件不为空,写入上传路径
if (!user.getImage().isEmpty())
{
// 设置要上传到服务器的目录
String path = request.getServletContext().getRealPath("/images");
// 获取上传的文件的文件名
String filename = user.getImage().getOriginalFilename();
// 在该目录下创建同名文件
File filepath = new File(path, filename);
// 判断路径是否存在,如果不存在就创建一个
if (!filepath.getParentFile().exists())
{
filepath.getParentFile().mkdirs();
}
// 将用户上传的图片保存到服务器目录中
user.getImage().transferTo(filepath);
// 将用户添加到model
model.addAttribute("filename",
user.getImage().getOriginalFilename());
System.out.println("上传文件路径:" + filepath.getAbsolutePath());
return "userInfo";
} else
{
return "error";
}
}

userInfo.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件下载</title>
</head>
<body>
<h3>文件下载</h3>
<br>
<a
href="javascript:window.location.href = 'download?filename=' + encodeURIComponent( '${requestScope.filename }' )">
${requestScope.filename } </a>
</body>
</html>

点击链接,download请求处理方法将会被调用,实现下载图片.
这里有一张图片

示例 SpringMVC的文件下载

download请求处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@GetMapping(value = "/download")
public ResponseEntity<byte[]> download(HttpServletRequest request,
@RequestParam("filename")
String filename,
@RequestHeader("User-Agent")
String userAgent) throws Exception
{
// 下载文件路径
String path = request.getServletContext().getRealPath("/images");
// 构建File
File file = new File(path + File.separator + filename);
// ok表示Http协议中的状态 200
BodyBuilder builder = ResponseEntity.ok();
// 内容长度
builder.contentLength(file.length());
// application/octet-stream : 二进制流数据(最常见的文件下载)。
builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
// 使用URLDecoder.decode对文件名进行解码
filename = URLEncoder.encode(filename, "UTF-8");
// 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】attachment 以附件形式
// 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
if (userAgent.indexOf("MSIE") > 0)
{
// 如果是IE,只需要用UTF-8字符集进行URL编码即可
builder.header("Content-Disposition",
"attachment; filename=" + filename);
} else
{
// 而FireFox、Chrome等浏览器,则需要说明编码的字符集
// 注意filename后面有个*号,在UTF-8后面有两个单引号!
builder.header("Content-Disposition",
"attachment; filename*=UTF-8''" + filename);
}
return builder.body(FileUtils.readFileToByteArray(file));
}

下载效果

这里有一张图片
SpringMVC提供了一个ResponseEntity类型,使用它可以很方便地定义返回的BodyBuilderHttpHeadersHttpStatus
download处理方法接收到页面传递的文件名 filename后,使用 Apache CommonsFileUpload组件的 FileUtils读取项目的 images文件夹下的该文件,并将其构建成ResponseEntity对象返回客户端下载。
使用 ResponseEntity对象,可以很方便地定义返回的 BodyBuilderHttpHeadersHttpStatus,BodyBuilder对象用来构建返回的Body;类型代表的是Http协议中的头信息;
Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。有关BodyBuilderMediaTypeHttpStatus类的详细信息参考Spring MVCAPI文档。