转自:http://www.2cto.com/kf/201406/312486.html
一、文件上传的原理
1、文件上传的前提:a、form表单的method必须是post
b、form表单的enctype必须是multipart/form-data(决定了POST请求方式,请求正文的数据类型)
注意:当表单的enctype是multipart/form-data,传统的获取请求参数的方法失效。
请求正文:(MIME协议进行描述的,正文是多部分组成的)
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="username"
wzhting
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="f1"; filename="C:\Documents and Settings\wzhting\妗岄潰\a.txt"
Content-Type: text/plain
aaaaaaaaaaaaaaaaaa
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="f2"; filename="C:\Documents and Settings\wzhting\妗岄潰\b.txt"
Content-Type: text/plain
bbbbbbbbbbbbbbbbbbb
-----------------------------7dd32c39803b2--
c、form中提供input的type是file类型的文件上传域
二、利用第三方 组件 实现文件上传
1、commons-fileupload组件:
jar:commons-fileupload.jar commons-io.jar
2、核心类或接口
DiskFileItemFactory:设置环境
public void setSizeThreshold(int?sizeThreshold) :设置缓冲区大小。默认是10Kb。
当上传的文件超出了缓冲区大小,fileupload组件将使用临时文件缓存上传文件
public void setRepository(java.io.File repository):设置临时文件的存放目录。默认是 系统 的临时文件存放目录。
ServletFileUpload:核心上传类(主要作用:解析请求的正文内容)
boolean isMultipartContent(HttpServletRequest?request):判断用户的表单的enctype是否是multipart/form-data类型的。
List parseRequest(HttpServletRequest request):解析请求正文中的内容
setFileSizeMax(4*1024*1024);//设置单个上传文件的大小
upload.setSizeMax(6*1024*1024);//设置总文件大小
FileItem:代表表单中的一个输入域。
boolean isFormField():是否是普通字段
String getFieldName:获取普通字段的字段名
String getString():获取普通字段的值
InputStream getInputStream():获取上传字段的输入流
String getName():获取上传的文件名
三、文件上传中要注意的9个问题
1、如何保证服务器的安全
把保存上传文件的目录放到WEB-INF目录中。
2、中文乱码问题
2.1普通字段的中文请求参数
String value = FileItem.getString("UTF-8");
2.2上传的文件名是中文
解决办法:request.setCharacterEncoding("UTF-8");
3、重名文件被覆盖的问题
System.currentMillions()+"_"+a.txt(乐观)
UUID+"_"+a.txt:保证文件名唯一
4、分目录存储上传的文件
方式一:当前日期建立一个文件夹,当前上传的文件都放到此文件夹中。
方式二:利用文件名的hash码打散目录来存储。
int hashCode = fileName.hashCode();
1001 1010 1101 0010 1101 1100 1101 1010
hashCode&0xf; 0000 0000 0000 0000 0000 0000 0000 1111 &
---------------------------------------------
0000 0000 0000 0000 0000 0000 0000 1010 取hashCode的后4位
0000~1111:整数0~15共16个
1001 1010 1101 0010 1101 1100 1101 1010
(hashCode&0xf0) 0000 0000 0000 0000 0000 0000 1111 0000 &
--------------------------------------------
0000 0000 0000 0000 0000 0000 1101 0000 >>4
--------------------------------------------
0000 0000 0000 0000 0000 0000 0000 1101
0000~1111:整数0~15共16个
5、限制用户上传的文件类型
通过判断文件的扩展名来限制是不可取的。
通过判断其Mime类型才靠谱。FileItem.getContentType();
6、如何限制用户上传文件的大小
6.1单个文件大小限制。超出了大小友好提示
抓异常进行提示:org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException
6.2总文件大小限制。超出了大小友好提示
抓异常进行提示:org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException
7、临时文件的问题
commons-fileupload组件不会删除超出缓存的临时文件。
FileItem.delete()方法删除临时文件。但一定要在关闭流之后。
8、多个文件上传时,没有上传内容的问题
if(fileName==null||"".equals(fileName.trim())){
continue;
}
9、上传进度检测
给ServletFileUpload注册一个进度监听器即可,把上传进度传递给页面去显示
//pBytesRead:当前以读取到的字节数
//pContentLength:文件的长度
//pItems:第几项
public void update(long pBytesRead, long pContentLength,
int pItems) {
System.out.println("已读取:"+pBytesRead+",文件大小:"+pContentLength+",第几项:"+pItems);
}
四:文件上传示例:
JSP代码:
1
2
3
4
5
6
|
<form action=
"${pageContext.request.contextPath}/servlet/UploadServlet"
method=
"post"
enctype=
"multipart/form-data"
>
用户名:<input type=
"text"
name=
"username"
><br>
文件
1
:<input type=
"file"
name=
"f1"
><br>
文件
2
:<input type=
"file"
name=
"f2"
><br>
<input type=
"submit"
value=
"保存"
>
</form>
|
servlet后台代码:
public
class
UploadServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
request.setCharacterEncoding(
"UTF-8"
);
response.setContentType(
"text/html;charset=UTF-8"
);
PrintWriter pout = response.getWriter();
try
{
// 得到存放上传文件的真实路径
String storePath = getServletContext()
.getRealPath(
"/WEB-INF/files"
);
// 设置环境
DiskFileItemFactory factory =
new
DiskFileItemFactory();
factory.setRepository(
new
File(getServletContext().getRealPath(
"/temp"
)));
//设置临时存放目录
// 判断一下form是否是enctype=multipart/form-data类型的
boolean
isMultipart = ServletFileUpload.isMultipartContent(request);
if
(!isMultipart) {
System.out.println(
"大傻鸟"
);
return
;
}
// ServletFileUpload核心类
ServletFileUpload upload =
new
ServletFileUpload(factory);
// upload.setProgressListener(new ProgressListener() {
// //pBytesRead:当前以读取到的字节数
// //pContentLength:文件的长度
// //pItems:第几项
// public void update(long pBytesRead, long pContentLength,
// int pItems) {
// System.out.println("已读取:"+pBytesRead+",文件大小:"+pContentLength+",第几项:"+pItems);
// }
//
// });
upload.setFileSizeMax(
4
*
1024
*
1024
);
// 设置单个上传文件的大小
upload.setSizeMax(
6
*
1024
*
1024
);
// 设置总文件大小
// 解析
List<fileitem> items = upload.parseRequest(request);
for
(FileItem item : items) {
if
(item.isFormField()) {
// 普通字段
String fieldName = item.getFieldName();
String fieldValue = item.getString(
"UTF-8"
);
System.out.println(fieldName +
"="
+ fieldValue);
}
else
{
// 得到MIME类型
String mimeType = item.getContentType();
// 只允许上传图片
if
(mimeType.startsWith(
"image"
)){
// 上传字段
InputStream in = item.getInputStream();
// 上传的文件名
String fileName = item.getName();
// C:\Documents and
if
(fileName==
null
||
""
.equals(fileName.trim())){
continue
;
}
// Settings\wzhting\妗岄潰\a.txt
// a.txt
fileName = fileName
.substring(fileName.lastIndexOf(
"\\"
) +
1
);
// a.txt
fileName = UUID.randomUUID() +
"_"
+ fileName;
System.out.println(request.getRemoteAddr()+
"=============="
+fileName);
// 构建输出流
// 打散存储目录
String newStorePath = makeStorePath(storePath, fileName);
// 根据
// /WEB-INF/files和文件名,创建一个新的存储路径
// /WEB-INF/files/1/12
String storeFile = newStorePath +
"\\"
+ fileName;
// WEB-INF/files/1/2/sldfdslf.txt
OutputStream out =
new
FileOutputStream(storeFile);
byte
b[] =
new
byte
[
1024
];
int
len = -
1
;
while
((len = in.read(b)) != -
1
) {
out.write(b,
0
, len);
}
out.close();
in.close();
item.delete();
//删除临时文件
}
}
}
}
catch
(org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException e) {
// 单个文件超出大小时的异常
pout.write(
"单个文件大小不能超出4M"
);
}
catch
(org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e) {
// 总文件超出大小时的异常
pout.write(
"总文件大小不能超出6M"
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
// 根据 /WEB-INF/files和文件名,创建一个新的存储路径 /WEB-INF/files/1/12
private
String makeStorePath(String storePath, String fileName) {
int
hashCode = fileName.hashCode();
int
dir1 = hashCode &
0xf
;
// 0000~1111:整数0~15共16个
int
dir2 = (hashCode &
0xf0
) >>
4
;
// 0000~1111:整数0~15共16个
String path = storePath +
"\\"
+ dir1 +
"\\"
+ dir2;
// WEB-INF/files/1/12
File file =
new
File(path);
if
(!file.exists())
file.mkdirs();
return
path;
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}</fileitem>
-----------------------------------------------------
五:文件下载
1.显示上述存储所有的照片信息:
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
|
//显示所有上传的文件,封装到域对象中,交给jsp去显示
public
class
ShowAllFilesServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
Map<string, string=
""
> map =
new
HashMap<string, string=
""
>();
//key:UUID文件名;value:老文件名
//得到存储文件的根目录
String storePath = getServletContext().getRealPath(
"/WEB-INF/files"
);
//递归遍历其中文件
File file =
new
File(storePath);
treeWalk(file,map);
//交给JSP去显示:如何封装数据.用Map封装。key:UUID文件名;value:老文件名
request.setAttribute(
"map"
, map);
request.getRequestDispatcher(
"/listFiles.jsp"
).forward(request, response);
}
//遍历/WEB-INF/files所有文件,把文件名放到map中
private
void
treeWalk(File file, Map<string, string=
""
> map) {
if
(file.isFile()){
//是文件
String uuidName = file.getName();
// UUID_a_a.txt//真实文件名
String oldName = uuidName.substring(uuidName.indexOf(
"_"
)+
1
);
map.put(uuidName, oldName);
}
else
{
//是一个目录
File[] fs = file.listFiles();
for
(File f:fs){
treeWalk(f,map);
}
}
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}</string,></string,></string,>
|
1
2
3
4
5
6
7
|
<h1>本站有以下好图片</h1>
<c:foreach items=
"${map}"
var=
"me"
>
<c:url value=
"/servlet/DownloadServlet"
var=
"url"
>
<c:param name=
"filename"
value=
"${me.key}"
></c:param>
</c:url>
${me.value} 下载<br>
</c:foreach>
|
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
|
public
class
DownloadServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
response.setContentType(
"text/html;charset=UTF-8"
);
OutputStream out = response.getOutputStream();
String filename = request.getParameter(
"filename"
);
//get请求方式
filename =
new
String(filename.getBytes(
"ISO-8859-1"
),
"UTF-8"
);
//中文编码
//截取老文件名
String oldFileName = filename.split(
"_"
)[
1
];
//得到存储路径
String storePath = getServletContext().getRealPath(
"/WEB-INF/files"
);
//得到文件的全部路径
String filePath = makeStorePath(storePath, filename)+
"\\"
+filename;
//判断文件是否存在
File file =
new
File(filePath);
if
(!file.exists()){
out.write(
"对比起!你要下载的文件可能已经不存在了"
.getBytes(
"UTF-8"
));
return
;
}
InputStream in =
new
FileInputStream(file);
//通知客户端以下载的方式打开
response.setHeader(
"Content-Disposition"
,
"attachment;filename="
+URLEncoder.encode(oldFileName,
"UTF-8"
));
byte
[] b =
new
byte
[
1024
];
int
len = -
1
;
while
((len=in.read(b))!=-
1
){
out.write(b,
0
, len);
}
in.close();
out.write(
"下载成功"
.getBytes(
"UTF-8"
));
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
private
String makeStorePath(String storePath, String fileName) {
int
hashCode = fileName.hashCode();
int
dir1 = hashCode &
0xf
;
// 0000~1111:整数0~15共16个
int
dir2 = (hashCode &
0xf0
) >>
4
;
// 0000~1111:整数0~15共16个
String path = storePath +
"\\"
+ dir1 +
"\\"
+ dir2;
// WEB-INF/files/1/12
File file =
new
File(path);
if
(!file.exists())
file.mkdirs();
return
path;
}
}
|