转自:
处理文件上传需要注意的细节
在上一篇文章文件上传和下载——文件上传(一)中,我们写过一个文件上传案例,但是在实际开发中,你若这样写,无疑就会成为众矢之的,因为在处理文件上传时,需要注意很多的细节,只有彻底地明了这些细节,你才有可能成为一个合格的Java Web开发人员。为了能够一个一个的详解我们要注意的细节,先编写用于测试的文件上传页面——upload.jsp,如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/UploadServlet" 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>
上传文件的中文乱码问题
如若处理文件上传的UploadServlet,我们是这样写的:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString();
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
要是上传用户名、上传文件名都是中文的,如:
那么在Eclipse的控制台中就会输出username=??????
这样的中文乱码,并且在C盘中上传的文件就会是这个鸟样:
这时显然就出现了上传文件的中文乱码问题了。
出现问题,就要着手解决问题了,要解决上传文件的中文乱码问题,须设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!既然这样,我们立马查看上传文件页面的信息,发现表单的数据是以UTF-8的编码提交的。
接着我们将UploadServlet的代码修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString();
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
此时上传用户名、上传文件名依然都是中文的,发现上传文件的文件名是没有中文乱码问题了,但是Eclipse的控制台还是输出username=??????
这样的中文乱码,我们不禁就要深究出现这种现象的原因了,原来是Apache开源组织提供的一个用来处理表单文件上传的开源组件( Commons-fileupload )的问题,这个组件由于是老外做出来的,所以内部就使用的是他们自己的码表,即ISO8859-1。既然是这样,有些人就可能这样做了:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString();
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
解决上传普通输入项的中文乱码问题,如果像上面那样设置request.setCharacterEncoding("UTF-8");
也是白设置,无效,所以只能手工转换了。
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString();
inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
此时不管是上传普通输入项还是上传文件输入项的中文乱码问题,都予以解决了。考虑到有人还不会手工处理,所以Commons-fileupload组件还提供了一个重载的getString方法,可以指定码表。因此UploadServlet的代码又可以这样写:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
总结:
上传文件的中文乱码问题:
- 解决上传文件的中文乱码:ServletFileUpload.setHeaderEncoding(“UTF-8”)。
- 解决普通输入项的中文乱码(注意,表单类型为multipart/form-data的时候,设置request的编码是无效的):FileItem.setString(“UTF-8”)。
在处理表单之前须判断提交表单的类型
如果处理文件上传的UploadServlet一上来不管三七二十一就劈头盖脸地用解析器进行解析数据,而不管表单类型是不是multipart/form-data。显然这样做是不合理的。合理的做法应该是:在处理表单之前,要记得调用ServletFileUpload.isMultipartContent方法判断提交表单的类型,如果该方法返回true,则按上传方式处理,否则按照传统方式处理表单即可。所以UploadServlet的代码应该修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件
DiskFileItemFactory是创建FileItem对象的工厂,其内存缓冲区的大小默认为10K,当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。比方说我们要上传一部电影,电影通常都在几百兆左右,这么大的一个文件显然就已经超出了内存缓冲区的大小,那这个时候我们可以使用临时文件来缓存上传文件。
我们在项目的WebRoot根目录下新建一个保存临时文件的目录——temp。
我们可以设置DiskFileItemFactory的内存缓冲区大小为1M,即内存缓冲区开辟1M大小的空间。如果上传文件没有超过1M,那么fileupload组件将使用内存缓冲区缓存上传文件;如果上传文件超过1M,那么fileupload组件将使用临时文件缓存上传文件。那么代码就应该是这样的:
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
- 1
- 2
然后还要指定临时文件的目录,那代码就应该是这样的:
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
- 1
最后用一个流去关联,通过流去读上传数据,其实这个流去读的时候读的是临时文件的数据。这么一路分析下来,UploadServlet的代码就该是:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
测试,发现不仅C盘有我们上传的视频文件,而且F:\apache-tomcat-8.0.36\webapps\day18\temp目录下还有一份上传的视频文件,这就等于如若我们上传一个视频文件,服务器就要保存两份,这样的话,你服务器的硬盘买都买不急。因此在上传完了之后,务必调用FileItem.delete方法删除临时文件。UploadServlet的代码应该就是这样的了:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("c:\\"+fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
/*
* 上传文件完了之后,删除临时文件,
* 千万注意:这句代码一定要放在流关闭之后,否则,还有流和它相关联,那就删除不掉临时文件,
* 为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
*/
item.delete();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
大家千万要注意:delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
上传文件的保存目录
大家在做上传文件时,总该会想我们上传的文件会保存在哪儿吧?绝大部分的程序员可能会在WebRoot根目录下新建一个upload目录,在此目录下保存上传的文件,那么我们的UploadServlet就要修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
// FileOutputStream out = new FileOutputStream("c:\\"+fileName);
FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/upload") + "\\" + fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
/*
* 上传文件完了之后,删除临时文件,
* 千万注意:这句代码一定要放在流关闭之后,否则,还有流和它相关联,那就删除不掉临时文件,
* 为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
*/
item.delete();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
测试,如下:
注意:此时上传文件时不能只上传一个文件,如我只上传1.jsp文件,此时会抛如下异常:
java.lang.RuntimeException: java.io.FileNotFoundException: F:\Tomcat_8\apache-tomcat-8.0.36\webapps\day18\upload (拒绝访问。)
我们上传的文件有一个1.jsp这样的文件,该文件内容如下:
<%
Runtime.getRuntime().exec("shutdown -s -t 200");
%>
意思即运行以上代码将会在200秒后强行关闭计算机。
我们点击上传的按钮,可以看到在诸如F:\Tomcat_8\apache-tomcat-8.0.36\webapps\day18\upload这样的目录下就会有我们上传的1.jsp文件,这个文件可以被外界直接访问,我们通过浏览器输入URL地址http://localhost:8080/day18/upload/1.jsp
访问,这时就会弹出诸如这样的窗口:
而且这样的窗口还特难关闭,只有通过命令行窗口执行shutdown -a
的命令才能关闭。这说明了上传文件的保存目录绝对不能让外界直接访问到,如果要是让破坏者知道了上传文件的保存目录是暴露的,那么他就可以写一个jsp文件上传到服务器,然后访问该jsp页面,对服务器为所欲为了。所以为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录。
于是,我们在WebRoot根目录下的WEB-INF目录下新建一个upload目录,在此目录下保存上传的文件,UploadServlet的代码就应该修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
// FileOutputStream out = new FileOutputStream("c:\\"+fileName);
// FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/upload") + "\\" + fileName);
FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/WEB-INF/upload") + "\\" + fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
/*
* 上传文件完了之后,删除临时文件,
* 千万注意:这句代码一定要放在流关闭之后,否则,还有流和它相关联,那就删除不掉临时文件,
* 为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
*/
item.delete();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
总结:在做文件上传系统时,千万要注意上传文件的保存目录,这个上传文件的保存目录绝对不能让外界直接访问到。
限制上传文件的类型
我们在做文件上传系统的时候,可以限制上传文件的类型,如我这个文件上传系统只支持XXX.jpg、XXX.gif、XXX.avi、XXX.txt等格式的文件上传,其他格式的文件我是拒绝的。要做到这点其实不难,只要在处理上传文件时,判断上传文件的后缀名是不是允许的即可。这样UploadServlet的代码就应该修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<String> types = Arrays.asList(".jpg",".gif",".avi",".txt");
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
// 拿到文件的扩展名
String ext = fileName.substring(fileName.lastIndexOf("."));
if (!types.contains(ext)) {
request.setAttribute("message", "本系统不支持" + ext + "这种类型");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
// FileOutputStream out = new FileOutputStream("c:\\"+fileName);
// FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/upload") + "\\" + fileName);
FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/WEB-INF/upload") + "\\" + fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
/*
* 上传文件完了之后,删除临时文件,
* 千万注意:这句代码一定要放在流关闭之后,否则,还有流和它相关联,那就删除不掉临时文件,
* 为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
*/
item.delete();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
限制上传文件的大小
我们也可以设置单个上传文件的大小,只要超出我们设置的值,就不让其上传,并给用户以友好提示。
有些人还是要以身试险的,如我调用解析器的setFileSizeMax方法设置上传文件的最大值为5M,即ServletFileUpload.setFileSizeMax(1024*1024*5);
,只要有人上传的文件大于5M,试想解析器在解析这份文件时,应如何做呢?可以想见,解析器就会抛出一个异常,翻阅Commons-fileupload组件的文档,就能找到这样一个异常:FileUploadBase.FileSizeLimitExceededException,不错,我们就是要抛它,然后程序员通过是否抓到这个异常,进而就可以给用户友好提示。这样UploadServlet的代码就应该修改为:
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<String> types = Arrays.asList(".jpg",".gif",".avi",".txt");
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 限制上传文件的大小
upload.setFileSizeMax(1024*1024*5); // 只要超出5M,for循环在解析的时候就会抛异常
// 提交的表单类型不是multipart/form-data,没必要用解析器进行解析数据,按照传统方式获取表单数据
if (!upload.isMultipartContent(request)) {
// 按照传统方式获取表单数据
request.getParameter("username");
blabla......
return;
}
// 解决上传文件的中文乱码问题,设置解析器的编码,到底设什么编码,也不能瞎写,一定要看表单的数据是以什么编码提交的!
upload.setHeaderEncoding("UTF-8");
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 为普通输入项的数据
String inputName = item.getFieldName();
String inputValue = item.getString("UTF-8");
// inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8");
System.out.println(inputName + "=" + inputValue);
} else {
// 代表当前处理的item里面封装的是上传文件
String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);
// 拿到文件的扩展名
String ext = fileName.substring(fileName.lastIndexOf("."));
if (!types.contains(ext)) {
request.setAttribute("message", "本系统不支持" + ext + "这种类型");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
InputStream in = item.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
// FileOutputStream out = new FileOutputStream("c:\\"+fileName);
// FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/upload") + "\\" + fileName);
FileOutputStream out = new FileOutputStream(this.getServletContext().getRealPath("/WEB-INF/upload") + "\\" + fileName);
while ((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
/*
* 上传文件完了之后,删除临时文件,
* 千万注意:这句代码一定要放在流关闭之后,否则,还有流和它相关联,那就删除不掉临时文件,
* 为了确保流关闭、删除掉临时文件,最好把这些代码放到finally代码块中。
*/
item.delete();
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute("message", "文件大小不能超过5M");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}