2.12.4 改进的Servlet API

2.12.4 改进的Servlet API

Servlet3还有一个改变是改进了部分API,这种改进很好地简化了Java Web开发。其中两个较大的改进是

  • HttpServletRequest增加了对文件上传的支持。
  • ServletContext允许通过编程的方式动态注册ServletFilter

HttpServletRequest文件上传方法

HttpServletRequest提供了如下两个方法来处理文件上传

  • Part getPart(String name):根据名称来获取文件上传域。
  • Collection<Part> getParms():获取所有的文件上传域

通过Part类对象来访问文件信息 将文件写入服务器

上面两个方法的返回值都涉及一个API:Part,每个Part对象对应于一个文件上传域,该对象提供了大量方法来访问上传文件的文件类型、大小、输入流等,并提供了一个write(String file)方法将上传文件写入服务器磁盘。

HTML文件域

为了向服务器上传文件,需要在表单里使用<input type="file">文件域,这个文件域会在HTML页面上产生一个单行文本框和一个“浏览”按钮,浏览者可通过该按钮选择需要上传的文件。

需要设置HTML form元素的enctype属性

除此之外,上传文件一定要为表单域设置enctype属性。

  • application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。
  • text/plain:这种编码方式当表单的action属性为mailto:URL的形式时比较方便,这种方式主要适用于直接通过表单发送邮件的方式。

如果将enctype设置为application/x-www-form-urlencoded,或不设置enctype属性,提交表单时只会发送文件域的文本框里的字符串,也就是浏览者所选择文件的绝对路径,对服务器获取该文件在客户端上的绝对路径没有任何作用,因为服务器不可能访问客户机的文件系统。

upload.jsp

下面定义了一个文件上传的页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html>
<html>
<head>
<title> 文件上传 </title>
</head>
<body>
<form method="post" action="upload" enctype="multipart/form-data">
文件名:<input type="text" id="name" name="name" /><br/>
选择文件:<input type="file" id="file" name="file" /><br/>
<input type="submit" value="上传" /><br/>
</form>
</body>
</html>

上面的页面中的表单需要设置enctype="multipart/form-data",这表明该表单可用于上传文件。
上面的表单中定义了两个表单域:

  • 一个普通的文本框,它将生成普通请求参数;
  • 一个文件上传域,它用于上传文件。

对于传统的文件上传需要借助于common-fileupload等工具,处理起来极为复杂,借助于Servlet3API,处理文件上传将变得十分简单。看下面的Servlet类代码

UploadServlet.java

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
package lee;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.util.*;

@WebServlet(name = "upload", urlPatterns = { "/upload" })
@MultipartConfig
public class UploadServlet extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter out = response.getWriter();
request.setCharacterEncoding("GBK");
// 获取普通请求参数
String name = request.getParameter("name");
out.println("普通的name参数为:" + name + "<br/>");
// 获取文件上传域
Part part = request.getPart("file");
// 获取上传文件的文件类型
out.println("上传文件的类型为:" + part.getContentType() + "<br/>");
// 获取上传文件的大小。
out.println("上传文件的大小为:" + part.getSize() + "<br/>");
// 获取该文件上传域的Header Name
Collection<String> headerNames = part.getHeaderNames();
// 遍历文件上传域的Header Name、Value
for (String headerName : headerNames) {
out.println(headerName + "--->" + part.getHeader(headerName) + "<br/>");
}
// 获取包含原始文件名的字符串
String fileNameInfo = part.getHeader("content-disposition");
// 提取上传文件的原始文件名
String fileName = fileNameInfo.substring(fileNameInfo.indexOf("filename=\"") + 10, fileNameInfo.length() - 1);
// 将上传的文件写入服务器
part.write(getServletContext().getRealPath("/uploadFiles") + "/" + fileName); // ①
}
}

上面Servlet使用了@MultipartConfig注解修饰,处理文件上传的Servlet应该使用该注解修饰。接下来该ServletHttpServletrequest就可通过getPart( String name)方法来获取文件上传域——就像获取普通请求参数一样。

web.xml中配置

Servlet所有注解相似的是,Servlet@MultipartConfig提供了相似的配置元素,同样可以通过在<servlet>元素中添加<multipart-config/>子元素来达到相同的效果
获取了上传文件对应的Part之后,可以非常简单地将文件写入服务器磁盘,如上面的①号代码所示。当然也可以通过Part获取所上传文件的文件类型、文件大小等各种详细信息。
例如选择一个*.png图片,然后单击上传将可看到如图2.52所示的页面。

上面的Servlet中将会把上传的文件保存到Web应用的根路径下的uploadFiles目录下,因此读者还应该在Web应用的根路径下创建uploadFiles目录

避免上传的文件同名

上面Servlet上传时保存的文件名直接使用了上传文件的原始文件名,在实际项目中一般不会这么做,因为可能多个用户可能上传同名的文件,这样将导致后面用户上传的文件覆盖前面用户上传的文件。在实际项目中可借助于java.util.UUID工具类生成文件名。

动态注册Servlet Filter

ServletContext则提供了如下方法来动态地注册ServetFilter,并允许动态设置Web应用的初始化参数。

  • 多个重载的addServlet()方法:动态地注册ServletE
  • 多个重载的addFilter()方法:动态地注册Filter
  • 多个重载的addListener()方法:动态地注册Listener
  • setInitParameter(String name, String value)方法:为Web应用设置初始化参数。