文件上传概述
1.文件上传必须满足的条件:
a.页面表单的method必须是post;
b.页面表单的enctype必须是multipart/form-data类型的
c.表单中提供<input type="file">上传文件域 注意:不适合上传特别大的文件;
2.请求正文内容的获取:
文件上传原理分析
1 <%@ page import="java.util.*" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 3 <html> 4 <head> 5 <title>文件上传</title> 6 <meta http-equiv="pragma" content="no-cache"> 7 <meta http-equiv="cache-control" content="no-cache"> 8 <meta http-equiv="expires" content="0"> 9 <!-- 10 <link rel="stylesheet" type="text/css" href="styles.css"> 11 --> 12 13 </head> 14 15 <body> 16 <!-- 17 enctype="application/x-www-form-urlencoded" 默认值 18 HTTP协议请求消息头:Content-Type,告知服务器,请求正文的内容的MIME类型(POST方式) 19 Content-Type的默认值:application/x-www-form-urlencoded 20 21 通过表单的enctype属性,实际上是指定的请求正文的MIME类型 22 23 --> 24 <form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" 25 enctype="multipart/form-data"> 26 姓名:<input type="text" name="name"/><br/> 27 靓照:<input type="file" name="photo"/><br/> 28 靓照:<input type="file" name="photo"/><br/> 29 <input type="submit" value="保存"/> 30 </form> 31 </body> 32 </html>
1 import java.io.IOException; 2 3 import javax.servlet.ServletException; 4 import javax.servlet.ServletInputStream; 5 import javax.servlet.http.HttpServlet; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 9 public class UploadServlet1 extends HttpServlet { 10 11 public void doGet(HttpServletRequest request, HttpServletResponse response) 12 throws ServletException, IOException { 13 ServletInputStream in = request.getInputStream();//获取请求正文的输入流 14 int len = -1; 15 byte b[] = new byte[1024]; 16 while((len=in.read(b))!=-1){ 17 System.out.println(new String(b,0,len)); 18 } 19 in.close(); 20 21 } 22 23 private void test1(HttpServletRequest request) { 24 /* 25 request.getParameter(String paramName) 26 只能获取请求正文为application/x-www-form-urlencoded类型的数据 27 28 name=abc&password=123 29 */ 30 31 32 String name = request.getParameter("name"); 33 String photo = request.getParameter("photo"); 34 System.out.println("name:"+name); 35 System.out.println("photo:"+photo);//不靠谱 36 } 37 38 public void doPost(HttpServletRequest request, HttpServletResponse response) 39 throws ServletException, IOException { 40 doGet(request, response); 41 } 42 43 }
enctype=”multipart/form-data”
可以使用流的方式获取请求正文内容,进一步解析;
借助第三方开发包实现文件上传
Servlet3.0内部提供对文件上传的支持(需要Tomcat7.0以上)
依赖:commons-io
1、拷贝jar包:
commons-fileupload-1.2.2.jar
commons-io-2.0.1.jar
2、fileupload组件的解析过程
3.主要的类:
1.DiskFileItemFactory:产生存在磁盘上的FileItem工厂;
public void setSizeThreshold(int SizeThreshold):设置上传文件时用的缓存大小,默认10KB;
public void setRepository(File repository):设置上传文件超出缓存大小的磁盘临时文件存放目录,默认是系统的临时文件存放目录;这里的目录都是服务器的
构造方法:DiskFileItemFactory():使用默认缓存,默认临时的文件存放目录
DiskFileItemFactory(int sizeThreshold,File repository):使用自己制定的缓存和临时文件存放目录;
2.ServletFileUpload:核心的解析请求类;
public static final Boolean isMultipartContent(java.servlet.http.HTTPServlet request);判断用户请求正文是否是multipart/form-data类型;
public List parseRequest(java.servlet.http.HTTPServletRequest request):解析请求对象,得到所有的表单输入域(每一个域用FileItem表示);
3.FileItem:代表每一个表单输入域;
4.上传代码:
代码撸上
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.List; 7 import java.util.UUID; 8 9 import javax.servlet.GenericServlet; 10 import javax.servlet.ServletException; 11 import javax.servlet.http.HttpServlet; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 15 import org.apache.commons.fileupload.FileItem; 16 import org.apache.commons.fileupload.FileUploadBase; 17 import org.apache.commons.fileupload.FileUploadException; 18 import org.apache.commons.fileupload.ProgressListener; 19 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 20 import org.apache.commons.fileupload.servlet.ServletFileUpload; 21 //借助commons-fileupload实现文件上传:入门案例 22 public class UploadServlet2 extends HttpServlet { 23 protected String storeDirectoryRealPath;//存放文件目录的真实路径 24 //获取初始化参数:上传文件的存放目录 25 public void init() throws ServletException { 26 String storeDirectory = "/upload";//默认的存放上传文件的位置 27 String value = getServletContext().getInitParameter("storeDirectory"); 28 if(value!=null){ 29 storeDirectory = value; 30 } 31 //该目录可能不存在 32 //得到文件的真实路径 33 storeDirectoryRealPath = getServletContext().getRealPath(storeDirectory); 34 //判断文件夹是否存在,不存在,创建它 35 File directory = new File(storeDirectoryRealPath); 36 if(!directory.exists()) 37 directory.mkdirs(); 38 } 39 public void doGet(HttpServletRequest request, HttpServletResponse response) 40 throws ServletException, IOException { 41 //判断用户的请求内容是否是mutlipart/form-data类型的 42 boolean isMultipart = ServletFileUpload.isMultipartContent(request); 43 if(!isMultipart){ 44 throw new RuntimeException("not a file upload request"); 45 } 46 try { 47 //创建DiskFileItemFactory,并设置缓存和临时文件的存放目录 48 DiskFileItemFactory factory = new DiskFileItemFactory(); 49 50 factory.setRepository(new File("d:/")); 51 52 //创建ServletFileUpload, 核心解析类 53 ServletFileUpload upload = new ServletFileUpload(factory); 54 // upload.setProgressListener(new ProgressListener() { 55 // /** 56 // * pBytesRead:已经读取的字节数 57 // * pContentLength:总上传文件的大小 58 // * pItems:当前处理的表单项 59 // */ 60 // public void update(long pBytesRead, long pContentLength, int pItems) { 61 //// double d = (pBytesRead+0.0)/pContentLength; 62 // System.out.println(pBytesRead+">>>"+pContentLength+">>"+pItems); 63 // } 64 // }); 65 66 // upload.setFileSizeMax(2*1024*1024);//设置单文件大小 67 // upload.setSizeMax(3*1024*1024);//设置总文件大小 68 //得到了List<FileItem> 69 List<FileItem> items = upload.parseRequest(request); 70 //遍历: 71 for(FileItem item:items){ 72 //遇到普通字段:做出自己的处理 73 if(item.isFormField()){ 74 processFormField(item); 75 }else{ 76 //遇到上传字段:实现文件的上传 77 78 String mimeType = item.getContentType();//获取上传输入域的MIME类型 79 if(!mimeType.startsWith("image")){ 80 //日志记录一下 81 continue; 82 } 83 processUploadField(item); 84 } 85 } 86 response.getWriter().write("上传成功!"); 87 }catch(FileUploadBase.FileSizeLimitExceededException e) { 88 response.getWriter().write("单文件不能超过2M"); 89 }catch(FileUploadBase.SizeLimitExceededException e){ 90 response.getWriter().write("总文件不能超过3M"); 91 }catch (FileUploadException e) { 92 e.printStackTrace(); 93 throw new RuntimeException("解析请求内容时遇到了错误"); 94 } 95 96 } 97 //处理上传文件 98 protected void processUploadField(FileItem item) { 99 try { 100 //获取输入流 101 // InputStream in = item.getInputStream(); 102 //得到上传文件的文件名 103 String fileName = getFileName(item.getName());// 浏览:C:UserswzhtingDesktopa.txt 浏览器:a.txt 104 //构建输出流 105 // OutputStream out = new FileOutputStream(storeDirectoryRealPath+"/"+fileName); 106 // int len = -1; 107 // byte b[] = new byte[1024]; 108 // while((len=in.read(b))!=-1){ 109 // out.write(b, 0, len); 110 // } 111 // in.close(); 112 // out.close(); 113 // item.delete();//删除临时文件 114 item.write(new File(storeDirectoryRealPath+"/"+fileName)); 115 116 } catch (IOException e) { 117 throw new RuntimeException("读取上传文件出错"); 118 }catch(Exception e){ 119 throw new RuntimeException("写文件出错"); 120 } 121 122 } 123 /** 124 * 如果文件路径是whole path,截取文件名 125 * @param fileName 126 */ 127 protected String getFileName(String fileName) { 128 if(fileName.indexOf("\")!=-1){ 129 fileName = fileName.substring(fileName.lastIndexOf("\")+1); 130 } 131 return fileName; 132 } 133 //处理普通字段内容 134 protected void processFormField(FileItem item) { 135 String fieldName = item.getFieldName();//普通字段名 136 String fieldValue = item.getString();//普通字段值 137 System.out.println(fieldName+"="+fieldValue); 138 } 139 140 public void doPost(HttpServletRequest request, HttpServletResponse response) 141 throws ServletException, IOException { 142 doGet(request, response); 143 } 144 145 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 7 <display-name></display-name> 8 <context-param> 9 <param-name>storeDirectory</param-name> 10 <param-value>/WEB-INF/files</param-value> 11 </context-param> 12 <filter> 13 <filter-name>SetAllCharacterEncodingFilter</filter-name> 14 <filter-class>com.itheima.filter.SetAllCharacterEncodingFilter</filter-class> 15 </filter> 16 <filter-mapping> 17 <filter-name>SetAllCharacterEncodingFilter</filter-name> 18 <url-pattern>/*</url-pattern> 19 </filter-mapping> 20 <servlet> 21 <servlet-name>UploadServlet2</servlet-name> 22 <servlet-class>com.itheima.servlet.UploadServlet2</servlet-class> 23 </servlet> 24 <servlet-mapping> 25 <servlet-name>UploadServlet2</servlet-name> 26 <url-pattern>/servlet/UploadServlet2</url-pattern> 27 </servlet-mapping> 28 <welcome-file-list> 29 <welcome-file>index.jsp</welcome-file> 30 </welcome-file-list> 31 </web-app>
文件上传时需要考虑几个问题
1.如何保证服务器安全问题:背景:将jsp文件上传,并访问,会出现安全问题
解决方案:把存放目录放到WEB-INF目录下;
2.中文乱码问题:
2.1:普通字段的中文乱码:解决方案:FileItem.getString(encoding):编码要与浏览器的编码一致
2.2:上传文件名是中文:解决方案:request.setCharacterEncoding(encoding):通知服务器,请求正文的编码类型
3.同一个文件夹下文件同名被覆盖问题:
1.解决方案:文件名唯一即可;a.txt---->UUID.randomUUID().toString()+"_"+a.txt(filename)
4.上传文件分目录存储:
1.分用户名目录存储:
2.按照日期分目录存储:
1 //按照日期分目录存储文件 2 protected String getFileName(String fileName) { 3 String name = super.getFileName(fileName);// a.txt 4 //按照日期创建目录 5 Date now = new Date(); 6 DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 7 String date = df.format(now); 8 //拼接该目录的真实存放路径 9 String directoryPath = storeDirectoryRealPath+"/"+date; 10 File directory = new File(directoryPath); 11 if(!directory.exists()) 12 directory.mkdirs(); 13 14 return date+"/"+UUID.randomUUID().toString()+"_"+name; 15 }
3.按照文件名hashCode计算存放目录 (二级目录)
1 //按照UUID文件名的hashCode计算存放目录 2 protected String getFileName(String fileName) { 3 String name = super.getFileName(fileName);// a.txt 4 String uuidFileName = UUID.randomUUID().toString()+"_"+name;// ae427cfa-4920-418a-992a-59553e54a302_a.txt 5 6 /* 7 hashCode: 8 1001 1100 0101 1010 1111 0001 9 0000 0000 0000 0000 0000 1111 &0xf 10 ----------------------------------- 11 0000 0000 0000 0000 0000 0001 取hash码的最低4位:0000~1111 十进制:0~15 12 hashCode: 13 1001 1100 0101 1010 1111 0001 14 0000 0000 0000 0000 1111 0000 &0xf0 15 ----------------------------------- 16 0000 0000 0000 0000 1111 0000 取hash码的最低5~8位 17 ----------------------------------- 18 >>4 19 0000 0000 0000 0000 0000 1111:0000~1111 十进制:0~15 20 */ 21 22 int hashCode = uuidFileName.hashCode(); 23 int dir1 = hashCode&0xf;//一级目录 24 int dir2 = (hashCode&0xf0)>>4;//二级目录 25 26 String newPath = dir1+"/"+dir2; 27 String directoryPath = storeDirectoryRealPath+"/"+newPath; 28 File directory = new File(directoryPath); 29 if(!directory.exists()) 30 directory.mkdirs(); 31 32 return newPath+"/"+uuidFileName; 33 }
5.限制文件的上传大小:
5.1:限制单个文件的上传大小
5.2:限制总文件上传大小(多个上传域)
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.List; 7 import java.util.UUID; 8 9 import javax.servlet.GenericServlet; 10 import javax.servlet.ServletException; 11 import javax.servlet.http.HttpServlet; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 15 import org.apache.commons.fileupload.FileItem; 16 import org.apache.commons.fileupload.FileUploadBase; 17 import org.apache.commons.fileupload.FileUploadException; 18 import org.apache.commons.fileupload.ProgressListener; 19 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 20 import org.apache.commons.fileupload.servlet.ServletFileUpload; 21 //借助commons-fileupload实现文件上传:入门案例 22 public class UploadServlet2 extends HttpServlet { 23 protected String storeDirectoryRealPath;//存放文件目录的真实路径 24 //获取初始化参数:上传文件的存放目录 25 public void init() throws ServletException { 26 String storeDirectory = "/upload";//默认的存放上传文件的位置 27 String value = getServletContext().getInitParameter("storeDirectory"); 28 if(value!=null){ 29 storeDirectory = value; 30 } 31 //该目录可能不存在 32 //得到文件的真实路径 33 storeDirectoryRealPath = getServletContext().getRealPath(storeDirectory); 34 //判断文件夹是否存在,不存在,创建它 35 File directory = new File(storeDirectoryRealPath); 36 if(!directory.exists()) 37 directory.mkdirs(); 38 } 39 public void doGet(HttpServletRequest request, HttpServletResponse response) 40 throws ServletException, IOException { 41 //判断用户的请求内容是否是mutlipart/form-data类型的 42 boolean isMultipart = ServletFileUpload.isMultipartContent(request); 43 if(!isMultipart){ 44 throw new RuntimeException("not a file upload request"); 45 } 46 try { 47 //创建DiskFileItemFactory,并设置缓存和临时文件的存放目录(服务器) 48 DiskFileItemFactory factory = new DiskFileItemFactory(); 49 50 factory.setRepository(new File("d:/")); 51 52 //创建ServletFileUpload, 核心解析类 53 ServletFileUpload upload = new ServletFileUpload(factory); 54 // upload.setProgressListener(new ProgressListener() { 55 // /** 56 // * pBytesRead:已经读取的字节数 57 // * pContentLength:总上传文件的大小 58 // * pItems:当前处理的表单项 59 // */ 60 // public void update(long pBytesRead, long pContentLength, int pItems) { 61 //// double d = (pBytesRead+0.0)/pContentLength; 62 // System.out.println(pBytesRead+">>>"+pContentLength+">>"+pItems); 63 // } 64 // }); 65 66 // upload.setFileSizeMax(2*1024*1024);//设置单文件大小 67 // upload.setSizeMax(3*1024*1024);//设置总文件大小 68 //得到了List<FileItem> 69 List<FileItem> items = upload.parseRequest(request); 70 //遍历: 71 for(FileItem item:items){ 72 //遇到普通字段:做出自己的处理 73 if(item.isFormField()){ 74 processFormField(item); 75 }else{ 76 //遇到上传字段:实现文件的上传 77 78 String mimeType = item.getContentType();//获取上传输入域的MIME类型 79 if(!mimeType.startsWith("image")){ 80 //日志记录一下 81 continue; 82 } 83 processUploadField(item); 84 } 85 } 86 response.getWriter().write("上传成功!"); 87 }catch(FileUploadBase.FileSizeLimitExceededException e) { 88 response.getWriter().write("单文件不能超过2M"); 89 }catch(FileUploadBase.SizeLimitExceededException e){ 90 response.getWriter().write("总文件不能超过3M"); 91 }catch (FileUploadException e) { 92 e.printStackTrace(); 93 throw new RuntimeException("解析请求内容时遇到了错误"); 94 } 95 96 } 97 //处理上传文件 98 protected void processUploadField(FileItem item) { 99 try { 100 //获取输入流 101 // InputStream in = item.getInputStream(); 102 //得到上传文件的文件名 103 String fileName = getFileName(item.getName());// 浏览:C:UserswzhtingDesktopa.txt 浏览器:a.txt 104 //构建输出流 105 // OutputStream out = new FileOutputStream(storeDirectoryRealPath+"/"+fileName); 106 // int len = -1; 107 // byte b[] = new byte[1024]; 108 // while((len=in.read(b))!=-1){ 109 // out.write(b, 0, len); 110 // } 111 // in.close(); 112 // out.close(); 113 // item.delete();//删除临时文件 114 item.write(new File(storeDirectoryRealPath+"/"+fileName)); 115 116 } catch (IOException e) { 117 throw new RuntimeException("读取上传文件出错"); 118 }catch(Exception e){ 119 throw new RuntimeException("写文件出错"); 120 } 121 122 } 123 /** 124 * 如果文件路径是whole path,截取文件名 125 * @param fileName 126 */ 127 protected String getFileName(String fileName) { 128 if(fileName.indexOf("\")!=-1){ 129 fileName = fileName.substring(fileName.lastIndexOf("\")+1); 130 } 131 return fileName; 132 } 133 //处理普通字段内容 134 protected void processFormField(FileItem item) { 135 String fieldName = item.getFieldName();//普通字段名 136 String fieldValue = item.getString();//普通字段值 137 System.out.println(fieldName+"="+fieldValue); 138 } 139 140 public void doPost(HttpServletRequest request, HttpServletResponse response) 141 throws ServletException, IOException { 142 doGet(request, response); 143 } 144 145 }
6.限制文件上传类型:
MIME类型+文件的扩展名
只允许上传图片:MIME设置(大类型/小类型)
7.临时文件的处理:
超出缓存会采用磁盘临时文件来缓存
FileItem.writer():使用该方法处理上传。临时文件会自动清理。(推荐)
手动删除:FileItem.delete();
8.文件上传进度条:(ajax)
1 upload.setProgressListener(new ProgressListener() { 2 /** 3 * pBytesRead:已经读取的字节数 4 * pContentLength:总上传文件的大小 5 * pItems:当前处理的表单项 6 */ 7 public void update(long pBytesRead, long pContentLength, int pItems) { 8 double d = (pBytesRead+0.0)/pContentLength; 9 System.out.println(pBytesRead+">>>"+pContentLength+">>"+pItems); 10 } 11 });
文件的下载
1 import java.io.IOException; 2 import java.io.UnsupportedEncodingException; 3 4 import javax.servlet.Filter; 5 import javax.servlet.FilterChain; 6 import javax.servlet.FilterConfig; 7 import javax.servlet.ServletException; 8 import javax.servlet.ServletRequest; 9 import javax.servlet.ServletResponse; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletRequestWrapper; 12 import javax.servlet.http.HttpServletResponse; 13 //解决GET和POST请求参数和响应输出的编码过滤器 14 public class SetAllCharacterEncodingFilter implements Filter { 15 private FilterConfig filterConfig; 16 public void init(FilterConfig filterConfig) throws ServletException { 17 this.filterConfig = filterConfig; 18 } 19 20 public void doFilter(ServletRequest req, ServletResponse resp, 21 FilterChain chain) throws IOException, ServletException { 22 HttpServletRequest request; 23 HttpServletResponse response; 24 try{ 25 request = (HttpServletRequest)req; 26 response = (HttpServletResponse)resp; 27 }catch(Exception e){ 28 throw new RuntimeException("non-http request"); 29 } 30 String encoding = "UTF-8";//默认参数 31 String value = filterConfig.getInitParameter("encoding"); 32 if(value!=null){ 33 encoding = value; 34 } 35 36 request.setCharacterEncoding(encoding);//POST请求方式 37 response.setCharacterEncoding(encoding); 38 response.setContentType("text/html;charset="+encoding); 39 MyHttpServletRequest mrequest = new MyHttpServletRequest(request); 40 chain.doFilter(mrequest, response); 41 } 42 43 public void destroy() { 44 45 } 46 47 } 48 class MyHttpServletRequest extends HttpServletRequestWrapper{ 49 private HttpServletRequest request; 50 public MyHttpServletRequest(HttpServletRequest request){ 51 super(request); 52 this.request = request; 53 } 54 public String getParameter(String name) { 55 String value = request.getParameter(name); 56 if(value==null) 57 return value; 58 try { 59 if("get".equalsIgnoreCase(request.getMethod())) 60 value = new String(value.getBytes("ISO-8859-1"),request.getCharacterEncoding()); 61 } catch (UnsupportedEncodingException e) { 62 e.printStackTrace(); 63 } 64 return value; 65 } 66 67 }
1 package com.itheima.servlet; 2 3 import java.io.File; 4 import java.io.UnsupportedEncodingException; 5 import java.text.DateFormat; 6 import java.text.SimpleDateFormat; 7 import java.util.Date; 8 import java.util.UUID; 9 10 import org.apache.commons.fileupload.FileItem; 11 12 public class UploadServlet3 extends UploadServlet2 { 13 14 protected void processFormField(FileItem item) { 15 String fieldName = item.getFieldName();//普通字段名 16 String fieldValue; 17 try { 18 fieldValue = item.getString("UTF-8");//普通字段值,指定编码,该编码和浏览器使用的编码要对应 19 System.out.println(fieldName+"="+fieldValue); 20 } catch (UnsupportedEncodingException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 } 25 26 //按照日期分目录存储文件 27 // protected String getFileName(String fileName) { 28 // String name = super.getFileName(fileName);// a.txt 29 // //按照日期创建目录 30 // Date now = new Date(); 31 // DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 32 // String date = df.format(now); 33 // //拼接该目录的真实存放路径 34 // String directoryPath = storeDirectoryRealPath+"/"+date; 35 // File directory = new File(directoryPath); 36 // if(!directory.exists()) 37 // directory.mkdirs(); 38 // 39 // return date+"/"+UUID.randomUUID().toString()+"_"+name; 40 // } 41 //按照UUID文件名的hashCode计算存放目录 42 protected String getFileName(String fileName) { 43 String name = super.getFileName(fileName);// a.txt 44 String uuidFileName = UUID.randomUUID().toString()+"_"+name;// ae427cfa-4920-418a-992a-59553e54a302_a.txt 45 46 /* 47 hashCode: 48 1001 1100 0101 1010 1111 0001 49 0000 0000 0000 0000 0000 1111 &0xf 50 ----------------------------------- 51 0000 0000 0000 0000 0000 0001 取hash码的最低4位:0000~1111 十进制:0~15 52 hashCode: 53 1001 1100 0101 1010 1111 0001 54 0000 0000 0000 0000 1111 0000 &0xf0 55 ----------------------------------- 56 0000 0000 0000 0000 1111 0000 取hash码的最低5~8位 57 ----------------------------------- 58 >>4 59 0000 0000 0000 0000 0000 1111:0000~1111 十进制:0~15 60 */ 61 62 int hashCode = uuidFileName.hashCode(); 63 int dir1 = hashCode&0xf;//一级目录 64 int dir2 = (hashCode&0xf0)>>4;//二级目录 65 66 String newPath = dir1+"/"+dir2; 67 String directoryPath = storeDirectoryRealPath+"/"+newPath; 68 File directory = new File(directoryPath); 69 if(!directory.exists()) 70 directory.mkdirs(); 71 72 return newPath+"/"+uuidFileName; 73 } 74 75 }
1 package com.itheima.servlet; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.net.URLEncoder; 9 10 import javax.servlet.ServletException; 11 import javax.servlet.http.HttpServlet; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 15 public class DownloadServlet extends HttpServlet { 16 17 public void doGet(HttpServletRequest request, HttpServletResponse response) 18 throws ServletException, IOException { 19 String uuidfilename = request.getParameter("filename");//get方式,注意编码问题 20 //计算文件所在的目录 21 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files"); 22 String path = makeDirs(storeDirectory, uuidfilename); 23 File file = new File(storeDirectory+"\"+path+"\"+uuidfilename); 24 if(file.exists()){ 25 //通知客户端以下载的方式打开 26 27 String filename = uuidfilename.substring(uuidfilename.indexOf("_")+1);//获取老文件名 哈里波波.jpg 28 response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(filename, "UTF-8")); 29 response.setHeader("Content-Type", "application/octet-stream");//通知客户端正文的MIME类型 30 //开始下载 31 InputStream in = new FileInputStream(file); 32 OutputStream out = response.getOutputStream(); 33 int len = -1; 34 byte b[] = new byte[1024]; 35 while((len=in.read(b))!=-1){ 36 out.write(b, 0, len); 37 } 38 in.close(); 39 }else{ 40 response.getOutputStream().write("您下载的资源不存在".getBytes("UTF-8")); 41 } 42 } 43 44 45 public String makeDirs(String storeDirectoryRealPath,String uuidFilename){ 46 int hashCode = uuidFilename.hashCode(); 47 int dir1 = hashCode&0xf;//一级目录 48 int dir2 = (hashCode&0xf0)>>4;//二级目录 49 50 String newPath = dir1+"/"+dir2; 51 String directoryPath = storeDirectoryRealPath+"/"+newPath; 52 File directory = new File(directoryPath); 53 if(!directory.exists()) 54 directory.mkdirs(); 55 return newPath; 56 } 57 58 public void doPost(HttpServletRequest request, HttpServletResponse response) 59 throws ServletException, IOException { 60 doGet(request, response); 61 } 62 63 }
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 //遍历:WEB-INF/files目录下的所有文件。 11 //交给JSP去显示 12 public class ShowAllFilesServlet extends HttpServlet { 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 Map<String, String> map = new HashMap<String, String>();// key:UUID的文件名 value:老文件名 a.txt 16 //遍历WEB-INF/files下的所有文件,并把文件名放到map中 17 18 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files"); 19 File file = new File(storeDirectory); 20 treeWalk(file,map); 21 22 //封装参数,交给JSP显示 23 request.setAttribute("map", map); 24 request.getRequestDispatcher("/listFile.jsp").forward(request, response); 25 } 26 //递归遍历,找出所有的文件名称 27 private void treeWalk(File file, Map<String, String> map) { 28 if(file.isFile()){ 29 String uuidFileName = file.getName();// UUID_a_a.txt 30 String fileName =uuidFileName.substring(uuidFileName.indexOf("_")+1);//a_a.txt 31 map.put(uuidFileName, fileName); 32 }else{ 33 File[] files = file.listFiles(); 34 for(File f:files){ 35 treeWalk(f, map); 36 } 37 } 38 } 39 40 public void doPost(HttpServletRequest request, HttpServletResponse response) 41 throws ServletException, IOException { 42 doGet(request, response); 43 } 44 45 }
1 <%@ page import="java.util.*" pageEncoding="UTF-8"%> 2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 4 <html> 5 <head> 6 <title>title</title> 7 <meta http-equiv="pragma" content="no-cache"> 8 <meta http-equiv="cache-control" content="no-cache"> 9 <meta http-equiv="expires" content="0"> 10 <!-- 11 <link rel="stylesheet" type="text/css" href="styles.css"> 12 --> 13 14 </head> 15 16 <body> 17 <h1>本站有以下不堪入目的图片</h1> 18 <c:forEach items="${map}" var="me"> 19 <c:url value="/servlet/DownloadServlet" var="url"> 20 <c:param name="filename" value="${me.key}"></c:param> 21 </c:url> 22 ${me.value} <a href="${url}">下载</a><br/> 23 </c:forEach> 24 </body> 25 </html>
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 7 <display-name></display-name> 8 <context-param> 9 <param-name>storeDirectory</param-name> 10 <param-value>/WEB-INF/files</param-value> 11 </context-param> 12 <filter> 13 <filter-name>SetAllCharacterEncodingFilter</filter-name> 14 <filter-class>com.itheima.filter.SetAllCharacterEncodingFilter</filter-class> 15 </filter> 16 <filter-mapping> 17 <filter-name>SetAllCharacterEncodingFilter</filter-name> 18 <url-pattern>/*</url-pattern> 19 </filter-mapping> 20 <servlet> 21 <servlet-name>UploadServlet1</servlet-name> 22 <servlet-class>com.itheima.servlet.UploadServlet1</servlet-class> 23 24 </servlet> 25 <servlet> 26 <servlet-name>UploadServlet2</servlet-name> 27 <servlet-class>com.itheima.servlet.UploadServlet2</servlet-class> 28 </servlet> 29 <servlet> 30 <servlet-name>UploadServlet3</servlet-name> 31 <servlet-class>com.itheima.servlet.UploadServlet3</servlet-class> 32 </servlet> 33 <servlet> 34 <servlet-name>ShowAllFilesServlet</servlet-name> 35 <servlet-class>com.itheima.servlet.ShowAllFilesServlet</servlet-class> 36 </servlet> 37 <servlet> 38 <servlet-name>DownloadServlet</servlet-name> 39 <servlet-class>com.itheima.servlet.DownloadServlet</servlet-class> 40 </servlet> 41 42 43 44 45 46 <servlet-mapping> 47 <servlet-name>UploadServlet1</servlet-name> 48 <url-pattern>/servlet/UploadServlet1</url-pattern> 49 </servlet-mapping> 50 <servlet-mapping> 51 <servlet-name>UploadServlet2</servlet-name> 52 <url-pattern>/servlet/UploadServlet2</url-pattern> 53 </servlet-mapping> 54 <servlet-mapping> 55 <servlet-name>UploadServlet3</servlet-name> 56 <url-pattern>/servlet/UploadServlet3</url-pattern> 57 </servlet-mapping> 58 <servlet-mapping> 59 <servlet-name>ShowAllFilesServlet</servlet-name> 60 <url-pattern>/servlet/ShowAllFilesServlet</url-pattern> 61 </servlet-mapping> 62 <servlet-mapping> 63 <servlet-name>DownloadServlet</servlet-name> 64 <url-pattern>/servlet/DownloadServlet</url-pattern> 65 </servlet-mapping> 66 <welcome-file-list> 67 <welcome-file>index.jsp</welcome-file> 68 </welcome-file-list> 69 </web-app>
观察者设计模式(监听器)
1、事件源:发生事件的对象
2、监听器:监听事件源上的发送的事件
3、事件对象:每发生一个事件都会产生一个事件对象,事件对象中封装了事件源
1 package com.itheima; 2 //事件源 3 public class Student { 4 private String name; 5 private StudentListener listener; 6 public Student(String name){ 7 this.name = name; 8 } 9 public void sleep(){ 10 System.out.println(name+"睡觉"); 11 } 12 public void study(){ 13 if(listener!=null){//注册了监听器 14 listener.preStudent(new StudentEvent(this)); 15 } 16 System.out.println(name+"学习Java编程"); 17 } 18 //注册监听器 19 public void addStudentListener(StudentListener listener){ 20 this.listener = listener; 21 } 22 public String getName() { 23 return name; 24 } 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 }
1 package com.itheima; 2 3 public class StudentDemo { 4 public static void main(String[] args) { 5 Student s = new Student("王斐"); 6 7 //注册事件监听器 8 s.addStudentListener(new MyStudentListener()); 9 10 s.sleep(); 11 s.study(); 12 } 13 } 14 class MyStudentListener implements StudentListener{ 15 16 public void preStudent(StudentEvent e) { 17 Student s = (Student)e.getSource(); 18 String name = s.getName(); 19 System.out.println(name+"一定要边学边练,还得做好笔记"); 20 } 21 22 public void preSleep(StudentEvent e) { 23 24 } 25 26 }
1 package com.itheima; 2 //事件对象 3 public class StudentEvent { 4 private Object source; 5 public StudentEvent(Object source){ 6 this.source = source; 7 } 8 public Object getSource() { 9 return source; 10 } 11 12 }
1 package com.itheima; 2 //监听器 3 public interface StudentListener { 4 void preStudent(StudentEvent e); 5 void preSleep(StudentEvent e); 6 }
Servlet规范中的八个监听器
7.1监听ServletContext、HttpSession、ServletRequest对象的创建和销毁的监听器
注册监听:web.xml
<listener></listener>
ServletContextListener
1 import javax.servlet.ServletContext; 2 import javax.servlet.ServletContextEvent; 3 import javax.servlet.ServletContextListener; 4 /* 5 非常有用: 6 ServletContext初始化一次,contextInitialized也就调用一次 7 完成系统启动时的初始化工作。 8 9 比如: 10 一个配置文件中定义了很多的JavaBean 11 <beans> 12 <bean id="xxDao1" class="XXXDao1Impl"/> 13 <bean id="xxDao2" class="XXXDao2Impl"/> 14 ... 15 </beans> 16 17 问题:用某个dao。在用dao实例时已经创建好的对象效率要高 18 19 在启动时,读取xml配置文件,把所有的bean都实例化,并放到内存中。 20 21 Spring: 22 23 24 */ 25 public class MyServletContextListener implements ServletContextListener { 26 27 public void contextInitialized(ServletContextEvent sce) { 28 ServletContext sc = sce.getServletContext(); 29 System.out.println("ServletContext对象创建了"+sc); 30 } 31 32 public void contextDestroyed(ServletContextEvent sce) { 33 ServletContext sc = sce.getServletContext(); 34 System.out.println("ServletContext对象销毁了"+sc); 35 } 36 37 }
HttpSessionListener
1 import javax.servlet.http.HttpSessionEvent; 2 import javax.servlet.http.HttpSessionListener; 3 /* 4 统计当前有多少个客户端在访问 5 */ 6 public class MyHttpSessionListener implements HttpSessionListener { 7 8 //第一次调用 request.getSession()时 9 public void sessionCreated(HttpSessionEvent se) { 10 System.out.println(se.getSession()+"创建了一个会话对象"); 11 } 12 13 public void sessionDestroyed(HttpSessionEvent se) { 14 System.out.println(se.getSession()+"会话对象销毁了"); 15 } 16 17 }
配置Httpsession的过时时间及存放位置:
ServletRequestListener:统计网站中资源的访问次数:
Map<String,Integer> key:资源的URL,value:访问次数
7.2监听ServletContext、HttpSession、ServletRequest对象中域中数据变化的监听器
- ServletContextAttributeListener
- HttpSessionAttributeListener
- ServletRequestAttributeListener
-
1 import javax.servlet.ServletContextAttributeEvent; 2 import javax.servlet.ServletContextAttributeListener; 3 4 public class MyServletContextAttributeListener implements 5 ServletContextAttributeListener { 6 7 public void attributeAdded(ServletContextAttributeEvent scab) { 8 System.out.println("添加了新数据"); 9 } 10 11 public void attributeRemoved(ServletContextAttributeEvent scab) { 12 System.out.println("移除了数据"); 13 14 } 15 16 public void attributeReplaced(ServletContextAttributeEvent scab) { 17 System.out.println("数据被替换了"); 18 } 19 20 }
7.3感知型监听器:不需要注册。谁实现了该类型的监听器,就能检测到自身的变化
HttpSessionBindingListener:感知自己何时被绑/解绑HttpSession域中。
HttpSessionActivationListener:感知自己何时随着HttpSession钝化/激活。
踢人案例
查看文件