对于文件上传,浏览器在上传的过程当中是将文件以流的形式提交到服务器端的,若是直接使用Servlet获取上传文件的输入流而后再解析里面的请求参数是比较麻烦,因此通常选择采用apache的开源工具common-fileupload这个文件上传组件。这个common-fileupload上传组件的jar包能够去apache官网上面下载,也能够在struts的lib文件夹下面找到,struts上传的功能就是基于这个实现的。common-fileupload是依赖于common-io这个包的,因此还须要下载这个包。web
1、开发环境搭建算法
建立一个FileUploadAndDownLoad项目,加入Apache的commons-fileupload文件上传组件的相关Jar包,以下图所示:数据库
2、实现文件上传apache
2.一、文件上传页面和消息提示页面数组
upload.jsp页面的代码以下:浏览器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<%@ page language=
"java"
pageEncoding=
"UTF-8"
%>
<!DOCTYPE HTML>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action=
"${pageContext.request.contextPath}/servlet/UploadHandleServlet"
enctype=
"multipart/form-data"
method=
"post"
>
上传用户:<input type=
"text"
name=
"username"
><br/>
上传文件1:<input type=
"file"
name=
"file1"
><br/>
上传文件2:<input type=
"file"
name=
"file2"
><br/>
<input type=
"submit"
value=
"提交"
>
</form>
</body>
</html>
|
message.jsp的代码以下:安全
1
2
3
4
5
6
7
8
9
10
11
|
<%@ page language=
"java"
pageEncoding=
"UTF-8"
%>
<!DOCTYPE HTML>
<html>
<head>
<title>消息提示</title>
</head>
<body>
${message}
</body>
</html>
|
2.二、处理文件上传的Servlet服务器
UploadHandleServlet的代码以下:
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
94
95
96
97
98
99
100
|
package
me.gacl.web.controller;
import
java.io.File;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.InputStream;
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.FileItem;
import
org.apache.commons.fileupload.disk.DiskFileItemFactory;
import
org.apache.commons.fileupload.servlet.ServletFileUpload;
public
class
UploadHandleServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
//获得上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不容许外界直接访问,保证上传文件的安全
String savePath =
this
.getServletContext().getRealPath(
"/WEB-INF/upload"
);
File file =
new
File(savePath);
//判断上传文件的保存目录是否存在
if
(!file.exists() && !file.isDirectory()) {
System.out.println(savePath+
"目录不存在,须要建立"
);
//建立目录
file.mkdir();
}
//消息提示
String message =
""
;
try
{
//使用Apache文件上传组件处理文件上传步骤:
//一、建立一个DiskFileItemFactory工厂
DiskFileItemFactory factory =
new
DiskFileItemFactory();
//二、建立一个文件上传解析器
ServletFileUpload upload =
new
ServletFileUpload(factory);
//解决上传文件名的中文乱码
upload.setHeaderEncoding(
"UTF-8"
);
//三、判断提交上来的数据是不是上传表单的数据
if
(!ServletFileUpload.isMultipartContent(request)){
//按照传统方式获取数据
return
;
}
//四、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每个FileItem对应一个Form表单的输入项
List<FileItem> list = upload.parseRequest(request);
for
(FileItem item : list){
//若是fileitem中封装的是普通输入项的数据
if
(item.isFormField()){
String name = item.getFieldName();
//解决普通输入项的数据的中文乱码问题
String value = item.getString(
"UTF-8"
);
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
System.out.println(name +
"="
+ value);
}
else
{
//若是fileitem中封装的是上传文件
//获得上传的文件名称,
String filename = item.getName();
System.out.println(filename);
if
(filename==
null
|| filename.trim().equals(
""
)){
continue
;
}
//注意:不一样的浏览器提交的文件名是不同的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf(
"\\"
)+
1
);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//建立一个文件输出流
FileOutputStream out =
new
FileOutputStream(savePath +
"\\"
+ filename);
//建立一个缓冲区
byte
buffer[] =
new
byte
[
1024
];
//判断输入流中的数据是否已经读完的标识
int
len =
0
;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while
((len=in.read(buffer))>
0
){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
out.write(buffer,
0
, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件
item.delete();
message =
"文件上传成功!"
;
}
}
}
catch
(Exception e) {
message=
"文件上传失败!"
;
e.printStackTrace();
}
request.setAttribute(
"message"
,message);
request.getRequestDispatcher(
"/message.jsp"
).forward(request, response);
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}
|
在Web.xml文件中注册UploadHandleServlet
1
2
3
4
5
6
7
8
9
|
<
servlet
>
<
servlet-name
>UploadHandleServlet</
servlet-name
>
<
servlet-class
>me.gacl.web.controller.UploadHandleServlet</
servlet-class
>
</
servlet
>
<
servlet-mapping
>
<
servlet-name
>UploadHandleServlet</
servlet-name
>
<
url-pattern
>/servlet/UploadHandleServlet</
url-pattern
>
</
servlet-mapping
>
|
运行效果以下:
文件上传成功以后,上传的文件保存在了WEB-INF目录下的upload目录,以下图所示:
2.三、文件上传的细节
上述的代码虽然能够成功将文件上传到服务器上面的指定目录当中,可是文件上传功能有许多须要注意的小细节问题,如下列出的几点须要特别注意的
一、为保证服务器安全,上传文件应该放在外界没法直接访问的目录下,好比放于WEB-INF目录下。
二、为防止文件覆盖的现象发生,要为上传文件产生一个惟一的文件名。
三、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
四、要限制上传文件的最大值。
五、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
针对上述提出的5点细节问题,咱们来改进一下UploadHandleServlet,改进后的代码以下:
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
package
me.gacl.web.controller;
import
java.io.File;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.InputStream;
import
java.util.List;
import
java.util.UUID;
import
javax.servlet.ServletException;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.apache.commons.fileupload.FileItem;
import
org.apache.commons.fileupload.FileUploadBase;
import
org.apache.commons.fileupload.ProgressListener;
import
org.apache.commons.fileupload.disk.DiskFileItemFactory;
import
org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* @ClassName: UploadHandleServlet
* @Description: TODO(这里用一句话描述这个类的做用)
* @author: 孤傲苍狼
* @date: 2015-1-3 下午11:35:50
*
*/
public
class
UploadHandleServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
//获得上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不容许外界直接访问,保证上传文件的安全
String savePath =
this
.getServletContext().getRealPath(
"/WEB-INF/upload"
);
//上传时生成的临时文件保存目录
String tempPath =
this
.getServletContext().getRealPath(
"/WEB-INF/temp"
);
File tmpFile =
new
File(tempPath);
if
(!tmpFile.exists()) {
//建立临时目录
tmpFile.mkdir();
}
//消息提示
String message =
""
;
try
{
//使用Apache文件上传组件处理文件上传步骤:
//一、建立一个DiskFileItemFactory工厂
DiskFileItemFactory factory =
new
DiskFileItemFactory();
//设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
factory.setSizeThreshold(
1024
*
100
);
//设置缓冲区的大小为100KB,若是不指定,那么缓冲区的大小默认是10KB
//设置上传时生成的临时文件的保存目录
factory.setRepository(tmpFile);
//二、建立一个文件上传解析器
ServletFileUpload upload =
new
ServletFileUpload(factory);
//监听文件上传进度
upload.setProgressListener(
new
ProgressListener(){
public
void
update(
long
pBytesRead,
long
pContentLength,
int
arg2) {
System.out.println(
"文件大小为:"
+ pContentLength +
",当前已处理:"
+ pBytesRead);
/**
* 文件大小为:14608,当前已处理:4096
文件大小为:14608,当前已处理:7367
文件大小为:14608,当前已处理:11419
文件大小为:14608,当前已处理:14608
*/
}
});
//解决上传文件名的中文乱码
upload.setHeaderEncoding(
"UTF-8"
);
//三、判断提交上来的数据是不是上传表单的数据
if
(!ServletFileUpload.isMultipartContent(request)){
//按照传统方式获取数据
return
;
}
//设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB
upload.setFileSizeMax(
1024
*
1024
);
//设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
upload.setSizeMax(
1024
*
1024
*
10
);
//四、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每个FileItem对应一个Form表单的输入项
List<FileItem> list = upload.parseRequest(request);
for
(FileItem item : list){
//若是fileitem中封装的是普通输入项的数据
if
(item.isFormField()){
String name = item.getFieldName();
//解决普通输入项的数据的中文乱码问题
String value = item.getString(
"UTF-8"
);
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
System.out.println(name +
"="
+ value);
}
else
{
//若是fileitem中封装的是上传文件
//获得上传的文件名称,
String filename = item.getName();
System.out.println(filename);
if
(filename==
null
|| filename.trim().equals(
""
)){
continue
;
}
//注意:不一样的浏览器提交的文件名是不同的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf(
"\\"
)+
1
);
//获得上传文件的扩展名
String fileExtName = filename.substring(filename.lastIndexOf(
"."
)+
1
);
//若是须要限制上传的文件类型,那么能够经过文件的扩展名来判断上传的文件类型是否合法
System.out.println(
"上传的文件的扩展名是:"
+fileExtName);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//获得文件保存的名称
String saveFilename = makeFileName(filename);
//获得文件的保存目录
String realSavePath = makePath(saveFilename, savePath);
//建立一个文件输出流
FileOutputStream out =
new
FileOutputStream(realSavePath +
"\\"
+ saveFilename);
//建立一个缓冲区
byte
buffer[] =
new
byte
[
1024
];
//判断输入流中的数据是否已经读完的标识 int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while
((len=in.read(buffer))>
0
){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
out.write(buffer,
0
, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件 //item.delete(); message = "文件上传成功!";
}
}
}
catch
(FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute(
"message"
,
"单个文件超出最大值!!!"
);
request.getRequestDispatcher(
"/message.jsp"
).forward(request, response);
return
;
}
catch
(FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute(
"message"
,
"上传文件的总的大小超出限制的最大值!!!"
);
request.getRequestDispatcher(
"/message.jsp"
).forward(request, response);
return
;
}
catch
(Exception e) {
message=
"文件上传失败!"
;
e.printStackTrace();
}
request.setAttribute(
"message"
,message);
request.getRequestDispatcher(
"/message.jsp"
).forward(request, response);
}
/**
* @Method: makeFileName
* @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
* @Anthor:孤傲苍狼
* @param filename 文件的原始名称
* @return uuid+"_"+文件的原始名称
*/
private
String makeFileName(String filename){
//2.jpg
//为防止文件覆盖的现象发生,要为上传文件产生一个惟一的文件名
return
UUID.randomUUID().toString() +
"_"
+ filename;
}
/**
* 为防止一个目录下面出现太多文件,要使用hash算法打散存储
* @Method: makePath
* @Description:
* @Anthor:孤傲苍狼
*
* @param filename 文件名,要根据文件名生成存储目录
* @param savePath 文件存储路径
* @return 新的存储目录
*/
private
String makePath(String filename,String savePath){
//获得文件名的hashCode的值,获得的就是filename这个字符串对象在内存中的地址
int
hashcode = filename.hashCode();
int
dir1 = hashcode&
0xf
;
//0--15
int
dir2 = (hashcode&
0xf0
)>>
4
;
//0-15
//构造新的保存目录
String dir = savePath +
"\\"
+ dir1 +
"\\"
+ dir2;
//upload\2\3 upload\3\5
//File既能够表明文件也能够表明目录 File file = new File(dir);
//若是目录不存在
if
(!file.exists()){
//建立目录
file.mkdirs();
}
return
dir;
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}
|
针对上述提出的5点小细节问题进行改进以后,咱们的文件上传功能就算是作得比较完善了。
3、文件下载
3.一、列出提供下载的文件资源
咱们要将Web应用系统中的文件资源提供给用户进行下载,首先咱们要有一个页面列出上传文件目录下的全部文件,当用户点击文件下载超连接时就进行下载操做,编写一个ListFileServlet,用于列出Web应用系统中全部下载文件。
ListFileServlet的代码以下:
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
|