上传和下载
一、文件上传必须要前提(记住)
- 1、表单的method属性必须是post
- 2、表单要提供<input type=”file” name=”photo”/>的上传输入域。
- 3、表单的enctype属性必须是multipart/form-data类型。
enctype:
作用:HTTP协议中要出现请求正文,method必须是POST方式。
他的作用就是相当于Content-Type的请求消息头的作用:告知服务器,请求正文的MIME类型。
二、文件上传的原理分析(理解)
enctype属性的常用取值
application/x-www-form-urlencoded(默认值)
username=abc&password=123&gender=male
String value = request.getParameter(String name)(服务器获取值的方法)
multipart/form-data
两种属性的情况,第二种我们就需要取值操作。。
三、借助第三方组件实现文件上传(会做)
为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。
使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
demo测试
1 import java.io.File;
2 import java.io.FileOutputStream;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import javax.servlet.ServletException;
10 import javax.servlet.http.HttpServlet;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.apache.commons.fileupload.FileItem;
15 import org.apache.commons.fileupload.FileUploadException;
16 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
17 import org.apache.commons.fileupload.servlet.ServletFileUpload;
18 //借助commons-fileupload实现文件上传:入门案例
19 public class UploadServlet2 extends HttpServlet {
20
21 public void doGet(HttpServletRequest request, HttpServletResponse response)
22 throws ServletException, IOException {
23 response.setContentType("text/html;charset=UTF-8");
24
25 //判断表单提交的内容是不是multipart/form-data类型的
26 boolean isMultipart = ServletFileUpload.isMultipartContent(request);
27 if(!isMultipart){
28 throw new RuntimeException("你这个傻叉,表单的enctype必须是multipart/form-data");
29 }
30 //解析请求中的正文内容:FileItem---代表者表单的每一项输入域
31 DiskFileItemFactory factory = new DiskFileItemFactory();
32 ServletFileUpload sfu = new ServletFileUpload(factory);
33 List<FileItem> items = new ArrayList<FileItem>();
34 try {
35 items = sfu.parseRequest(request);
36 } catch (FileUploadException e) {
37 throw new RuntimeException("解析上传内容失败");
38 }
39 //遍历:
40 for(FileItem item:items){
41 //是普通字段:把字段名和值打印到控制台上
42 if(item.isFormField()){
43 processFormField(item);
44 }else{
45 //是上传字段:把上传的文件保存在应用的\files目录中
46 try {
47 processUploadField(item);
48 } catch (Exception e) {
49 e.printStackTrace();
50 }
51 }
52 }
53
54 response.getOutputStream().write("上传成功".getBytes("UTF-8"));
55 }
56 //是上传字段:把上传的文件保存在应用的\files目录中
57 private void processUploadField(FileItem item) throws Exception {
58 //得到保存文件的真实目录
59 String storeDirectory = getServletContext().getRealPath("/files");
60 //不在的话:创建他
61 File root = new File(storeDirectory);
62 if(!root.exists()){
63 root.mkdirs();
64 }
65 //得到文件名
66 String filename = item.getName();
67 //输入流和输出流:搞
68 InputStream in = item.getInputStream();
69 OutputStream out = new FileOutputStream(new File(storeDirectory, filename));
70 int len = -1;
71 byte b[] = new byte[1024];
72 while((len=in.read(b))!=-1){
73 out.write(b, 0, len);
74 }
75 in.close();
76 out.close();
77 }
78
79 //是普通字段:把字段名和值打印到控制台上
80 private void processFormField(FileItem item) {
81 String fieldName = item.getFieldName();
82 String fieldValue = item.getString();
83 System.out.println(fieldName+"="+fieldValue);
84 }
85
86 public void doPost(HttpServletRequest request, HttpServletResponse response)
87 throws ServletException, IOException {
88 doGet(request, response);
89 }
90
91 }
四、文件上传编程时要考虑的几个问题(思考)
1:保护上传文件和服务器的安全
解决办法:把保存上传文件的目录存放在让客户端直接访问不到的地方
2、避免同一文件夹下的文件重名
把文件名改为唯一的即可。
3、避免同一文件夹下的文件过多
分目录进行存储
方案一:按照日期生成存储目录
方案二:按照文件名的hash码计算子目录:a\b
4、中文乱码问题
a、普通字段的中文乱码
FileItem.getString(String charset);
b、上传的中文文件名乱码问题
request.setCharacterEncoding("UTF-8");//只能解决上传的文件名是中文的乱码
5、限制上传文件的大小
Web方式下不适合上传特别大的文件
a、限制单个文件大小
b、限制总文件大小:一次多文件上传时
6、限制上传文件的类型
控制上传文件的扩展名和判断上传文件的MIME类型。
IE浏览器:上传时把文件扩展名改掉后,他的MIME类型没有变
a.txt MIME:text/plain
a.jpg (还是那个文本) MIME:text/plain
火狐:a.txt MIME:text/plain
a.jpg(还是那个文本) MIME:image/jpg
更加专业:取得文件的二进制,判断文件类型。
7、多文件上传时,没有选择上传文件的问题
用JS进行判断
服务器端验证:不要判断FileItem是不是空,因为不管用户上不上传,该对象都不是空。要通过文件的文件名来判断。
8、临时文件的问题
默认情况下,上传的文件超过10Kb,commons-fileupload就会采用临时文件。
在上传时,流关闭后,调用FileItem.delete()删除临时文件。
完整demo
1 //上传时要考虑的问题
2 public class UploadServlet3 extends HttpServlet {
3
4 public void doGet(HttpServletRequest request, HttpServletResponse response)
5 throws ServletException, IOException {
6 request.setCharacterEncoding("UTF-8");//只能解决上传的文件名是中文的乱码
7 response.setContentType("text/html;charset=UTF-8");
8
9 //判断表单提交的内容是不是multipart/form-data类型的
10 boolean isMultipart = ServletFileUpload.isMultipartContent(request);
11 if(!isMultipart){
12 throw new RuntimeException("你这个傻叉,表单的enctype必须是multipart/form-data");
13 }
14 //解析请求中的正文内容:FileItem---代表者表单的每一项输入域
15 DiskFileItemFactory factory = new DiskFileItemFactory();
16
17 // factory.setSizeThreshold(10*1024);//设置缓存的大小。默认10kb。
18 // factory.setRepository(new File("d:/"));//设置临时文件存放的目录。默认值是系统的临时文件目录
19
20 ServletFileUpload sfu = new ServletFileUpload(factory);
21 // sfu.setFileSizeMax(4*1024*1024);//限制单个文件上传大小不能超过4M
22 // sfu.setSizeMax(6*1024*1024);//限制总文件大小不能超过6M
23 List<FileItem> items = new ArrayList<FileItem>();
24 try {
25 items = sfu.parseRequest(request);
26 } catch(FileUploadBase.FileSizeLimitExceededException e){
27 response.getOutputStream().write("单个文件大小不能超出4M".getBytes("UTF-8"));
28 }catch(FileUploadBase.SizeLimitExceededException e){
29 response.getOutputStream().write("总文件大小不能超出6M".getBytes("UTF-8"));
30 }catch (FileUploadException e) {
31 e.printStackTrace();
32 throw new RuntimeException("解析上传内容失败");
33 }
34 //遍历:
35 for(FileItem item:items){
36 //是普通字段:把字段名和值打印到控制台上
37 if(item.isFormField()){
38 processFormField(item);
39 }else{
40 //是上传字段:把上传的文件保存在应用的\files目录中
41 try {
42 processUploadField(item);
43 } catch (Exception e) {
44 e.printStackTrace();
45 }
46 }
47 }
48
49 response.getOutputStream().write("上传成功".getBytes("UTF-8"));
50 }
51 //是上传字段:把上传的文件保存在应用的\files目录中
52 private void processUploadField(FileItem item) throws Exception {
53 //得到保存文件的真实目录
54 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
55 //不在的话:创建他
56 File root = new File(storeDirectory);
57 if(!root.exists()){
58 root.mkdirs();
59 }
60 //得到文件名
61 String filename = item.getName();// C:\Users\wzhting\Desktop\a.txt a.txt 与浏览器有关
62 if(filename!=null){
63
64 //判断文件的扩展名只能上传文本文件
65 // String extensionName = FilenameUtils.getExtension(filename);
66 // if(!"txt".equals(extensionName)){
67 // return;
68 // }
69
70 filename = FilenameUtils.getName(filename);// a.txt
71 // System.out.println(filename+"MIME类型:"+item.getContentType());
72 }
73 //把文件名改为唯一的
74 filename = UUID.randomUUID()+"_"+filename;
75
76 //计算一个子目录:存储文件的
77 String childDirectory = makeChildDirectory(storeDirectory,filename);
78
79
80 //输入流和输出流:搞
81 /*
82 InputStream in = item.getInputStream();
83 OutputStream out = new FileOutputStream(new File(storeDirectory+File.separator+childDirectory, filename));
84 int len = -1;
85 byte b[] = new byte[1024];
86 while((len=in.read(b))!=-1){
87 out.write(b, 0, len);
88 }
89 in.close();
90 out.close();
91 item.delete();// 删除临时文件
92 */
93 item.write(new File(storeDirectory+File.separator+childDirectory, filename));
94 }
95 按照文件名的哈希吗生成子目录:返回的是子目录名称 1\3
96 private String makeChildDirectory(String storeDirectory, String filename) {
97 int hashCode = filename.hashCode();
98 int dir1 = hashCode&0xf;//取0~3位
99 int dir2 = (hashCode&0xf0)>>4;//取4~7位
100 /*
101 1101 1111 0101 0111 1101 1111 0101 0111 hashCode
102 0000 0000 0000 0000 0000 0000 0000 1111 &
103 ---------------------------------------------
104 0000 0000 0000 0000 0000 0000 0000 0111 范围:0000~1111转成10进制:0~15
105
106 1101 1111 0101 0111 1101 1111 0101 0111 hashCode
107 0000 0000 0000 0000 0000 0000 1111 0000 &0xf0
108 ---------------------------------------------
109 0000 0000 0000 0000 0000 0000 0101 0000 >>4
110 ---------------------------------------------
111 0000 0000 0000 0000 0000 0000 0000 0101 范围:0000~1111转成10进制:0~15
112 */
113 String childDirectory = dir1+File.separator+dir2;// 1\3
114 File directory = new File(storeDirectory, childDirectory);
115 if(!directory.exists()){
116 directory.mkdirs();
117 }
118 return childDirectory;
119 }
120 //按照日期生成子目录:返回的是子目录名称 2014-11-28
121 private String makeChildDirectory(String storeDirectory) {
122 Date now = new Date();
123 String childDirectory = new SimpleDateFormat("yyyy-MM-dd").format(now);
124 //判断子目录在不在:不在创建他
125 File directory = new File(storeDirectory, childDirectory);
126 if(!directory.exists()){
127 directory.mkdirs();
128 }
129 return childDirectory;
130 }
131 //是普通字段:把字段名和值打印到控制台上
132 private void processFormField(FileItem item) {
133 try {
134 String fieldName = item.getFieldName();
135 String fieldValue = item.getString("UTF-8");
136 System.out.println(fieldName+"="+fieldValue);
137 } catch (UnsupportedEncodingException e) {
138 e.printStackTrace();
139 }
140 }
141
142 public void doPost(HttpServletRequest request, HttpServletResponse response)
143 throws ServletException, IOException {
144 doGet(request, response);
145 }
146
147 }
五、文件的下载(会做)
查询可下载的所有文件
1 import java.io.File;
2 import java.io.IOException;
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import javax.servlet.ServletException;
7 import javax.servlet.http.HttpServlet;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10 //查询可下载的所有文件
11 public class ShowAllFilesServlet extends HttpServlet {
12
13 public void doGet(HttpServletRequest request, HttpServletResponse response)
14 throws ServletException, IOException {
15 //key:uuidFilename value:oldFilename
16 //key:1c2d33b7-b4e4-4cc0-80e4-f2d8a1c94e94_1_1.jpg
17 //value:1_1.jpg
18
19 Map<String, String> map = new HashMap<String, String>();
20 //得到WEB-INF/files的真实目录,对里面的文件进行递归
21 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
22 File root = new File(storeDirectory);
23 treeWalk(root,map);
24 //Map中就存了文件的文件名
25 request.setAttribute("map", map);
26 request.getRequestDispatcher("/listFiles.jsp").forward(request, response);
27 }
28
29 private void treeWalk(File root, Map<String, String> map) {
30 if(root.isFile()){
31 String uuidFilename = root.getName();
32 String oldFilename = uuidFilename.substring(uuidFilename.indexOf("_")+1);
33 map.put(uuidFilename, oldFilename);
34 }else{
35 File[] files = root.listFiles();
36 for(File file:files){
37 treeWalk(file, map);
38 }
39 }
40 }
41
42 public void doPost(HttpServletRequest request, HttpServletResponse response)
43 throws ServletException, IOException {
44 doGet(request, response);
45 }
46
47 }
js页面
<body>
<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} <a href="${url}">下载</a><br/>
</c:forEach>
</body>
下载
1 import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.net.URLEncoder;
7
8 import javax.servlet.ServletException;
9 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 //实现文件的下载
13 public class DownloadServlet extends HttpServlet {
14
15 public void doGet(HttpServletRequest request, HttpServletResponse response)
16 throws ServletException, IOException {
17 response.setContentType("text/html;charset=UTF-8");
18 //得到要下载的文件名
19 String uuidFilename = request.getParameter("filename");//5eaad6f3-ce5e-44c2-8885-538650e890de_%e7%be%8e%e5%a5%b3.jpg
20 //get方式提交过来的请求参数
21 uuidFilename = new String(uuidFilename.getBytes("ISO-8859-1"),"UTF-8");//5eaad6f3-ce5e-44c2-8885-538650e890de_美女.jpg
22 //他在哪个目录中
23 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
24 String childDirectory = makeChildDirectory(storeDirectory,uuidFilename);
25
26 //判断该文件在不在
27 File file = new File(storeDirectory+File.separator+childDirectory,uuidFilename);
28 if(!file.exists()){
29 response.getOutputStream().write("文件已经不存在了".getBytes("UTF-8"));
30 return;
31 }
32 //文件存在
33 //截取原来文件名
34 String oldFileName = uuidFilename.substring(uuidFilename.indexOf("_")+1);//有可能是中文
35 response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName, "UTF-8"));
36
37 InputStream in = new FileInputStream(file);
38 OutputStream out = response.getOutputStream();
39 int len = -1;
40 byte b[] = new byte[1024];
41 while((len=in.read(b))!=-1){
42 out.write(b, 0, len);
43 }
44 in.close();
45 // response.getOutputStream().write("下载成功".getBytes("UTF-8"));
46 }
47
48 public void doPost(HttpServletRequest request, HttpServletResponse response)
49 throws ServletException, IOException {
50 doGet(request, response);
51 }
52 private String makeChildDirectory(String storeDirectory, String filename) {
53 int hashCode = filename.hashCode();
54 int dir1 = hashCode&0xf;//取0~3位
55 int dir2 = (hashCode&0xf0)>>4;//取4~7位
56 String childDirectory = dir1+File.separator+dir2;// 1\3
57 File directory = new File(storeDirectory, childDirectory);
58 if(!directory.exists()){
59 directory.mkdirs();
60 }
61 return childDirectory;
62 }
63 }