过滤器、拦截器

  1. 过滤器

为什么需用到过滤器?

项目开发中,经常会涉及到重复代码的实现!

注册 ----à Servlet 【1. 设置编码】 ----à  JSP

修改 ----àServlet 【1. 设置编码】 ---à  JSP

其他,

         如判断用户是否登陆,只有登陆才能有操作权限!

         涉及到重复判断: 获取session,取出session数据,判断是否为空,为空说明没有登陆,不能操作; 只有登陆后,才能操作!

如何解决:

  1. 抽取重复代码,封装
  2. 每个用到重复代码的地方,手动的调用!

过滤器,设计执行流程:

  1. 用户访问服务器
  2. 过滤器: 对Servlet请求进行拦截
  3. 先进入过滤器, 过滤器处理
  4. 过滤器处理完后, 在放行, 此时,请求到达Servlet/JSP
  5. Servlet处理
  6. Servlet处理完后,再回到过滤器, 最后在由tomcat服务器相应用户;

(过滤器就像回家的门!)

过滤器,HelloWorld案例

Javax.servlet.*;

|-- interface  Filter  及过滤器

开发步骤:

  1. 写一个普通java类,实现Filter接口
  2. 配置过滤器

过滤器执行流程

OOAD   面向对象的分析与设计

使用RationRose 时序图

过滤器相关Api

|-- interface  Filter                          过滤器核心接口

         Void  init(filterConfig);    初始化方法,在服务器启动时候执行

Void  doFilter(request,response,filterChain);   过滤器拦截的业务处理方法

Void destroy();                       销毁过滤器实例时候调用

 

|-- interface  FilterConfig   获取初始化参数信息

        

String

getInitParameter(java.lang.String name)

Enumeration

getInitParameterNames()

 

|-- interface  FilterChain     过滤器链参数;一个个过滤器形成一个执行链;

         void doFilter(ServletRequest request, ServletResponse response)  ;  执行下一个过滤器或放行

/**
 * 过滤器,测试
 * @author Jie.Yuan
 *
 */
public class HelloFilter implements Filter{
	
	// 创建实例
	public HelloFilter(){
		System.out.println("1. 创建过滤器实例");
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("2. 执行过滤器初始化方法");
		
		// 获取过滤器在web.xml中配置的初始化参数
		String encoding = filterConfig.getInitParameter("encoding");
		System.out.println(encoding);
		
		// 获取过滤器在web.xml中配置的初始化参数 的名称
		Enumeration<String> enums =  filterConfig.getInitParameterNames();
		while (enums.hasMoreElements()){
			// 获取所有参数名称:encoding、path
			String name = enums.nextElement();
			// 获取名称对应的值
			String value = filterConfig.getInitParameter(name);
			System.out.println(name + "\t" + value);
		}
	}

	// 过滤器业务处理方法: 在请求到达servlet之前先进入此方法处理公用的业务逻辑操作
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("3. 执行过滤器业务处理方法");
		// 放行 (去到Servlet)
		// 如果有下一个过滤器,进入下一个过滤器,否则就执行访问servlet
		chain.doFilter(request, response);
		
		System.out.println("5. Servlet处理完成,又回到过滤器");
	}

	@Override
	public void destroy() {
		System.out.println("6. 销毁过滤器实例");
	}

}
<!-- 过滤器配置 -->
	<filter>
		<!-- 配置初始化参数 -->
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>path</param-name>
			<param-value>c:/...</param-value>
		</init-param>
	
		<!-- 内部名称 -->
		<filter-name>hello_filter</filter-name>
		<!-- 过滤器类的全名 -->
		<filter-class>cn.itcast.a_filter_hello.HelloFilter</filter-class>
	</filter>
	<filter-mapping>
		<!-- filter内部名称 -->
		<filter-name>hello_filter</filter-name>
		<!-- 拦截所有资源 -->
		<url-pattern>/*</url-pattern>
	</filter-mapping>

对指定的请求拦截

/*   表示拦截所有的请求

<filter-mapping>

        <filter-name>hello_filter2</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

默认拦截的类型:(直接访问或者重定向)

<dispatcher>REQUEST</dispatcher>

拦截转发:

                  <dispatcher>FORWARD</dispatcher>

拦截包含的页面(RequestDispatcher.include(/page.jsp);    page.jsp也执行拦截)

                    <dispatcher>INCLUDE</dispatcher>

拦截声明式异常信息:

                    <dispatcher>ERROR</dispatcher>

<!-- 配置第二个过滤器 -->
	<!-- 演示: 拦截指定的请求 -->
	<filter>
		<filter-name>hello_filter2</filter-name>
		<filter-class>cn.itcast.a_filter_hello.HelloFilter2</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>hello_filter2</filter-name>
		<!-- 1. 拦截所有
		<url-pattern>/*</url-pattern>
		 -->
		 
		 <!-- 2. 拦截指定的jsp 
		 <url-pattern>/index.jsp</url-pattern>
		 <url-pattern>/list.jsp</url-pattern>
		 -->
		 <!-- 拦截所有的jsp
		 <url-pattern>*.jsp</url-pattern>
		  -->
		  <!-- 3. 根据servlet的内部名称拦截
		  <servlet-name>IndexServlet</servlet-name>
		   -->
		  <!-- 拦截指定的servlet 
		  <url-pattern>/index</url-pattern>
		  -->
		  
		  <!-- 4. 指定拦截指定的类型 -->
		  <url-pattern>/*</url-pattern>
		  <dispatcher>REQUEST</dispatcher>
		  <dispatcher>FORWARD</dispatcher>
	</filter-mapping>

共性问题:

  1. 过滤器:方法参数没有自动命名,说明没有关联源码

--à 关联tomcat或servlet源代码

      2. 连接池: 多刷新几次,报错!

                 -->连接没关

                          QueryRunner qr = new QueryRunner();

                          qr.update(con,sql);

                          // 这里con一定要关闭

            --> 注意:dataSource 确定一个项目创建一次

                          QueryRunner qr = new QueryRunner(dataSource);

               --> 修改连接池参数配置

       3 .  编码

                  // 设置POST提交的请求的编码

                  request.setCharacterEncoding("UTF-8");

                  // 设置相应体的编码

                  response.setCharacterEncoding("UTF-8");

                  // 设置页面打开时候时候的编码格式、 设置相应体的编码

                  response.setContentType("text/html;charset=UTF-8");

                  开发中:

                          工作区间编码、项目编码、request/response、数据库编码一致!

2.案例

过滤器-编码统一处理

几乎每一个Servlet都要涉及编码处理:处理请求数据中文问题!

【GET/POST】

每个servlet都要做这些操作,把公用的代码抽取-过滤器实现!

 

代码实现思路:

  1. Login.jsp  登陆,输入“中文”
  2. LoginServlet.java   直接处理登陆请求
  3. EncodingFilter.java   过滤器处理请求数据编码:GET/POST
过滤器:

/**
 * 编码处理统一写到这里(servlet中不需要再处理编码)
 * @author Jie.Yuan
 *
 */
public class EncodingFilter implements Filter {

	// 过滤器业务处理方法:处理的公用的业务逻辑操作
	@Override
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		
		// 转型
		final HttpServletRequest request = (HttpServletRequest) req;    
		HttpServletResponse response = (HttpServletResponse) res;
		
		// 一、处理公用业务
		request.setCharacterEncoding("UTF-8");					// POST提交有效
		response.setContentType("text/html;charset=UTF-8");
		
		/*
		 * 出现GET中文乱码,是因为在request.getParameter方法内部没有进行提交方式判断并处理。
		 * String name = request.getParameter("userName");
		 * 
		 * 解决:对指定接口的某一个方法进行功能扩展,可以使用代理!
		 *      对request对象(目标对象),创建代理对象!
		 */
		HttpServletRequest proxy =  (HttpServletRequest) Proxy.newProxyInstance(
				request.getClass().getClassLoader(), 		// 指定当前使用的累加载器
				new Class[]{HttpServletRequest.class}, 		// 对目标对象实现的接口类型
				new InvocationHandler() {					// 事件处理器
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						// 定义方法返回值
						Object returnValue = null;
						// 获取方法名
						String methodName = method.getName();
						// 判断:对getParameter方法进行GET提交中文处理
						if ("getParameter".equals(methodName)) {
							
							// 获取请求数据值【 <input type="text" name="userName">】
							String value = request.getParameter(args[0].toString());	// 调用目标对象的方法
							
							// 获取提交方式
						String methodSubmit = request.getMethod(); // 直接调用目标对象的方法
							
							// 判断如果是GET提交,需要对数据进行处理  (POST提交已经处理过了)
					   if ("GET".equals(methodSubmit)) {
								if (value != null && !"".equals(value.trim())){
									// 处理GET中文
								value = new String(value.getBytes("ISO8859-1"),"UTF-8");
								}
							} 
							return value;
						}
						else {
							// 执行request对象的其他方法
							returnValue = method.invoke(request, args);
						}
						
						return returnValue;
					}
				});
		
		// 二、放行 (执行下一个过滤器或者servlet)
		chain.doFilter(proxy, response);		// 传入代理对象
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}

	@Override
	public void destroy() {
		
	}
}
过滤器配置:
<!-- 编码处理过滤器配置 -->
	<filter>
		<filter-name>encoding</filter-name>
		<filter-class>cn.itcast.a_loginFilter.EncodingFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>encoding</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
Servlet:
public class LoginServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// 获取请求数据 
		String name = request.getParameter("userName");
		System.out.println("用户:" + name);
	}

过滤器-无效数据过滤

模拟:论坛过滤敏感词汇!

实现思路:

  1. Dis.jsp    讨论区页面
  2. DisServlet.java    处理提交

   ---》 获取请求参数

   ---》 保存到request域

  -----》 跳转dis.jsp  【从request取数据显示(处理后)】

  1. DataFilter.java   过滤器

----》编码

---》 无效数据处理

      即: 在上一个案例基础上,再添加无效数据过滤的相关代码!

JSP引入ckeditor组件:客户端组件,便于用户输入内容!
JSP
<!-- 引入ckeditor组件(给用户输入提供方便) --> 
	<script src="${pageContext.request.contextPath }/ckeditor/ckeditor.js"></script>
	<link rel="stylesheet" href="${pageContext.request.contextPath }/ckeditor/samples/sample.css">
	
<body>
  	${requestScope.content }
  
  	<form name="frmDis" action="${pageContext.request.contextPath }/dis" method="post">
  	  发表评论: <textarea class="ckeditor" rows="6" cols="30" name="content"></textarea>
  	  
  	  <br/>
  	  <input type="submit" value="评论" >
  	</form>
  </body>
Filter:
在上个过滤器案例的基础上,增加如下代码:
// 中文数据已经处理完: 下面进行无效数据过滤   
							//【如何value中出现dirtyData中数据,用****替换】  
							for (String data : dirtyData) {
								// 判断当前输入数据(value), 是否包含无效数据
								if (value.contains(data)){
									value = value.replace(data, "*****");
								}
							}

DAY21

  1. 监听器

    1.1 概述

监听器: 主要是用来监听特定对象的创建或销毁、属性的变化的!

                   是一个实现特定接口的普通java类!

 

对象:

         自己创建自己用 (不用监听)

         别人创建自己用 (需要监听)

Servlet中哪些对象需要监听?

         request / session / servletContext

         分别对应的是request监听器、session相关监听器、servletContext监听器

监听器(listener)

监听器接口:

一、监听对象创建/销毁的监听器接口

         Interface ServletRequestListener     监听request对象的创建或销毁

         Interface HttpSessionListener        监听session对象的创建或销毁

         Interface ServletContextListener     监听servletContext对象的创建或销毁

二、监听对象属性的变化

         Interface ServletRequestAttributeListener 监听request对象属性变化: 添加、移除、修改

         Interface HttpSessionAttributeListener    监听session对象属性变化: 添加、移除、修改

Interface ServletContextAttributeListener  监听servletContext对象属性变化

三、session相关监听器

         Interface HttpSessionBindingListener   监听对象绑定到session上的事件   

    Interface HttpSessionActivationListener(了解) 监听session序列化及反序列化的事件

 

404(路径写错)

500(服务器错误,调试)

1.2 生命周期监听器

声明周期监听器: 监听对象的创建、销毁的过程!

监听器开发步骤:

  1. 写一个普通java类,实现相关接口;
  2. 配置(web.xml)

 A.      ServletRequestListener 

 监听request对象的常见和销毁。

代码:

/**
 *  监听request对象的创建或销毁
 * @author Jie.Yuan
 *
 */
public class MyRequestListener implements ServletRequestListener{

	// 对象销毁
	@Override
	public void requestDestroyed(ServletRequestEvent sre) {
		// 获取request中存放的数据
		Object obj = sre.getServletRequest().getAttribute("cn");
		System.out.println(obj);
		System.out.println("MyRequestListener.requestDestroyed()");
	}

	// 对象创建
	@Override
	public void requestInitialized(ServletRequestEvent sre) {
		System.out.println("MyRequestListener.requestInitialized()");
	}
}

Web.xml
<!-- 监听request对象创建、销毁 -->
	<listener>
		<listener-class>cn.itcast.a_life.MyRequestListener</listener-class>
	</listener>

B. HttpSessionListener

监听session对象的创建或销毁。

package cn.itcast.a_life;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 监听Session对象创建、销毁
 * @author Jie.Yuan
 *
 */
public class MySessionListener implements HttpSessionListener{

	// 创建
	@Override
	public void sessionCreated(HttpSessionEvent se) {
		System.out.println("MySessionListener.sessionCreated()");
	}

	// 销毁
	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		System.out.println("MySessionListener.sessionDestroyed()");
	}

}

C. ServletContextListener

监听servletContext对象的创建或销毁

package cn.itcast.a_life;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 监听ServletContext对象创建或销毁
 * @author Jie.Yuan
 *
 */
public class MyServletContextListener implements ServletContextListener{

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("MyServletContextListener.contextDestroyed()");
	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("1..........MyServletContextListener.contextInitialized()");
	}

}

1.3 属性监听器

监听:request/session/servletContext对象属性的变化!

ServletRequestAttributeListener

HttpSessionAttributeListener

ServletContextAttributeListener

总结:先写类,实现接口;  再配置

package cn.itcast.b_attr;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * 监听session对象属性的变化
 * @author Jie.Yuan
 *
 */
public class MySessionAttrListener implements HttpSessionAttributeListener {

	// 属性添加
	@Override
	public void attributeAdded(HttpSessionBindingEvent se) {
		// 先获取session对象
		HttpSession session = se.getSession();
		// 获取添加的属性
		Object obj = session.getAttribute("userName");
		// 测试
		System.out.println("添加的属性:" + obj);
	}

	// 属性移除
	@Override
	public void attributeRemoved(HttpSessionBindingEvent se) {
		System.out.println("属性移除");
	}

	// 属性被替换
	@Override
	public void attributeReplaced(HttpSessionBindingEvent se) {
		// 获取sesison对象
		HttpSession session = se.getSession();
		
		// 获取替换前的值
		Object old = se.getValue();
		System.out.println("原来的值:" + old);
		
		// 获取新值
		Object obj_new = session.getAttribute("userName");
		System.out.println("新值:" + obj_new);
		
	}

}

1.4 其他监听器: session相关监听器

HttpSessionBindingListener 

监听对象绑定/解除绑定到sesison上的事件!

步骤:

         对象实现接口; 再把对象绑定/解除绑定到session上就会触发监听代码。

作用:

         (上线提醒!)

/**
 * 监听此对象绑定到session上的过程,需要实现session特定接口
 * @author Jie.Yuan
 *
 */
public class Admin implements HttpSessionBindingListener {
   … 省略get/set
// 对象放入session
	@Override
	public void valueBound(HttpSessionBindingEvent event) {
		System.out.println("Admin对象已经放入session");
	}
	// 对象从session中移除
	@Override
	public void valueUnbound(HttpSessionBindingEvent event) {
		System.out.println("Admin对象从session中移除!");
	}
}

 

2. 国际化

Javaweb增强:过滤器、监听器、国际化、文件上传下载、javaMail

国际化又简称为 i18n:internationalization

Locale  本地化

Java提供了一个本地化的对象!封装当前语言、国家、环境等特征!


public class App {

	@Test
	//1. 本地化对象:Locale
	// 封装语言、国家信息的对象,有java.util提供
	public void testLocale() throws Exception {
		// 模拟中国语言等环境
		//Locale locale = Locale.CHINA;
		Locale locale = Locale.getDefault();			// 当前系统默认的语言环境
		System.out.println(locale.getCountry());   		// CN  国家的简称
		System.out.println(locale.getDisplayCountry()); // 国家名称
		System.out.println(locale.getLanguage());		// zh 语言简称
		
		// 模拟美国国家
		Locale l_us = Locale.US;
		System.out.println(l_us.getCountry());
		System.out.println(l_us.getDisplayCountry());
	}
}

国际化

静态数据国际化

网站中显示的固定文本的国际化: “用户名”“密码“

国际化的软件:

  1. 存储所有国家显示的文本的字符串
    1. 文件: properties
    2. 命名:  基础名_语言简称_国家简称.properties

例如:msg_zh_CN.properties     存储所有中文

           Msg_en_US.properties    存储所有英文

  1. 程序中获取

ResourceBundle类,可以读取国际化的资源文件!

动态文本国际化

中文:1987-09-19   ¥1000

英文: Sep/09 1987  $100

  1. 数值,货币,时间,日期等数据由于可能在程序运行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来,而是需要特殊处理。Java 中提供了解决这些问题的 API (位于 java.util 包和 java.text 包中)
// 国际化 - 静态数据
	@Test
	public void testI18N() throws Exception {
		
		// 中文语言环境
		Locale locale = Locale.US;
		
		// 创建工具类对象ResourceBundle
		ResourceBundle bundle = ResourceBundle.getBundle("cn.itcast.f_i18n.msg", locale);
		// 根据key获取配置文件中的值
		System.out.println(bundle.getString("hello"));
		System.out.println(bundle.getString("username"));
		System.out.println(bundle.getString("pwd"));
		
	}
	
	// 国际化 - 动态文本 - 0. 概述
	@Test
	public void testI18N2() throws Exception {
		// 国际化货币
		NumberFormat.getCurrencyInstance();
		// 国际化数字
		NumberFormat.getNumberInstance();
		// 国际化百分比
		NumberFormat.getPercentInstance();  
		// 国际化日期
		//DateFormat.getDateTimeInstance(dateStyle, timeStyle, aLocale)
	}
	
	// 国际化 - 动态文本 - 1. 国际化货币
	@Test
	public void testI18N3() throws Exception {
		// 模拟语言环境
		Locale locale = Locale.CHINA;
		// 数据准备
		double number = 100;
		// 工具类
		NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
		// 国际化货币
		String m = nf.format(number);
		// 测试
		System.out.println(m);
	}
	
	//面试题:  代码计算:  $100 * 10  
	@Test
	public void eg() throws Exception {
		String str = "$100";
		int num = 10;
		
		// 1. 分析str值是哪一国家的货币
		Locale us = Locale.US;
		// 2. 国际化工具类
		NumberFormat nf = NumberFormat.getCurrencyInstance(us);
		// 3. 解析str国币
		Number n = nf.parse(str);
		
		System.out.println(n.intValue() * num);
	}
	
	// 国际化 - 动态文本 - 2. 国际化数值
	@Test
	public void testI18N4() throws Exception {
		// 模拟语言环境
		Locale locale = Locale.CHINA;
		NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
		String str = nf.format(1000000000);
		System.out.println(str);
	}
	
	// 国际化 - 动态文本 - 3. 国际化日期
	/*
	 * 日期
	 * 	  FULL   2015年3月4日 星期三
	 * 	  LONG   2015年3月4日
	 * 	  FULL   2015年3月4日 星期三
	 *    MEDIUM 2015-3-4
	 *    SHORT  15-3-4
	 *    
	 * 时间
	 * 	  FULL   下午04时31分59秒 CST
	 * 	  LONG   下午04时32分37秒
	 *    MEDIUM 16:33:00
	 *    SHORT  下午4:33
	 *    
	 * 
	 */
	@Test
	public void testI18N5() throws Exception {
		
		// 日期格式
		int dateStyle = DateFormat.SHORT;
		// 时间格式
		int timeStyle = DateFormat.SHORT;
		
		// 工具类
		DateFormat df = 
			DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.CHINA);		
		String date = df.format(new Date());
		
		System.out.println(date);
	}
	
	// 面试2: 请将时间值:09-11-28 上午10时25分39秒 CST,反向解析成一个date对象。
	@Test
	public void eg2() throws Exception {
		String str = "09-11-28 上午10时25分39秒 CST";
		// 创建DateFormat工具类,国际化日期
		DateFormat df = DateFormat.getDateTimeInstance(
				DateFormat.SHORT, DateFormat.FULL, Locale.getDefault());
		Date d = df.parse(str);
		
		System.out.println(d);
	}
	

Jsp页面国际化

<html>
  <head>
  	<%
  	ResourceBundle bundle = ResourceBundle.getBundle("cn.itcast.f_i18n.msg",request.getLocale());
  	%>
    <title><%=bundle.getString("title") %></title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
  </head>
  
  <body>
  <form name="frmLogin" action="${pageContext.request.contextPath }/admin?method=login" method="post">
  	<table align="center" border="1">
  		<tr>
  			<td><%=bundle.getString("username") %></td>
  			<td>
  				<input type="text" name="userName">
  			</td>
  		</tr>
  		<tr>
  			<td><%=bundle.getString("pwd") %></td>
  			<td>
  				<input type="password" name="pwd">
  			</td>
  		</tr>
  		<tr>
  			<td>
  				<input type="submit" value="<%=bundle.getString("submit") %>">
  			</td>
  		</tr>
  	</table>
  </form>
  </body>
</html>

Jsp页面国际化 – 使用jstl标签

JSTL标签:

         核心标签库

         国际化与格式化标签库

         数据库标签库(没用)

         函数库

<fmt:setLocale value=""/>        设置本地化对象

      <fmt:setBundle basename=""/>     设置工具类

      <fmt:message></fmt:message>     显示国际化文本

格式化数值

<fmt:formatNumber pattern="#.##" value="100.99"></fmt:formatNumber>

格式化日期:

<fmt:formatDate pattern="yyyy-MM-dd" value="${date}"/>

<html>
  <head>
  	<!-- 一、设置本地化对象 -->
  	<fmt:setLocale value="${pageContext.request.locale}"/>
  	<!-- 二、设置工具类 -->
  	<fmt:setBundle basename="cn.itcast.f_i18n.msg" var="bundle"/>

    <title><fmt:message key="title" bundle="${bundle}"></fmt:message></title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
  </head>
  
  <body>
  <form name="frmLogin" action="${pageContext.request.contextPath }/admin?method=login" method="post">
  	<table align="center" border="1">
  		<tr>
  			<td><fmt:message key="username" bundle="${bundle}"></fmt:message></td>
  			<td>
  				<input type="text" name="userName">
  			</td>
  		</tr>
  		<tr>
  			<td><fmt:message key="pwd" bundle="${bundle}"></fmt:message></td>
  			<td>
  				<input type="password" name="pwd">
  			</td>
  		</tr>
  		<tr>
  			<td>
  				<input type="submit" value="<fmt:message key="submit" bundle="${bundle}"/>">
  			</td>
  		</tr>
  	</table>
  </form>
  </body>
</html>

=========================================================

DAY23

回顾:

  • 监听器
  1. 生命周期监听器

ServletRequestListener

HttpSessionListener

ServletContextListener

  1. 属性监听器

ServletRequestAttributeListener

HttpSessionAttributeListener

ServletContextAttributeListener

  1. Session相关监听器
  2. 案例: 显示在线用户!

监听器:ServletContextListener/HttpSessionListener

  • 国际化
  1. 国际化资源文件: 基础名_语言_国家.properties   【通用】

基础名.properties   (默认)

  1. 获取国际化文本
    1. ResourceBundle工具类
  2. JSP页面国际化

Jstl提供的“国际化与格式化标签库” 

1. 文件上传与下载

1.1 文件上传

案例:

         注册表单/保存商品等相关模块!

         --->注册选择头像 / 商品图片

         (数据库:存储图片路径 / 图片保存到服务器中指定的目录)

文件上传,要点:

前台:

         1. 提交方式:post

         2. 表单中有文件上传的表单项: <input type=”file” />

         3. 指定表单类型:

                       默认类型:enctype="application/x-www-form-urlencoded"

                  文件上传类型:multipart/form-data

手动实现文件上传

<body>	
  	 <form name="frm_test" action="${pageContext.request.contextPath }/upload" method="post" enctype="multipart/form-data">
  	 	 用户名:<input type="text" name="userName">  <br/>
  	 	文件:   <input type="file" name="file_img">   <br/>
  	 	
  	 	<input type="submit" value="注册">
   	 </form>
  </body>
public class UploadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		/*
		request.getParameter(""); // GET/POST
		request.getQueryString(); // 获取GET提交的数据 
		request.getInputStream(); // 获取post提交的数据   */
		
		/***********手动获取文件上传表单数据************/
		
		//1. 获取表单数据流
		InputStream in =  request.getInputStream();
		//2. 转换流
		InputStreamReader inStream = new InputStreamReader(in, "UTF-8");
		//3. 缓冲流
		BufferedReader reader = new BufferedReader(inStream);
		// 输出数据
		String str = null;
		while ((str = reader.readLine()) != null) {
			System.out.println(str);
		}
		
		// 关闭
		reader.close();
		inStream.close();
		in.close();
	}

输出结果:

------WebKitFormBoundaryGoQviatB7iM1dhPr
Content-Disposition: form-data; name="userName"       【FileItem】

Jack
------WebKitFormBoundaryGoQviatB7iM1dhPr
Content-Disposition: form-data; name="file_img"; filename="reamde.txt"
Content-Type: text/plain                                     【FileItem】


test!!!!!!!!!!!!!
test!!!!!!!!!!!!!
------WebKitFormBoundaryGoQviatB7iM1dhPr--

最终获取数据,要对上面的结果进行解析!

文件上传,在开发中经常用,每次都写解析程序!(工具类)

也可以使用开源的文件上传组件-FileUpload组件!

Apache提供的文件上传组件:FileUpload组件

文件上传功能开发中比较常用,apache也提供了文件上传组件!

FileUpload组件:

         1. 下载源码

         2. 项目中引入jar文件

                          commons-fileupload-1.2.1.jar  【文件上传组件核心jar包】

                          commons-io-1.4.jar     【封装了对文件处理的相关工具类】


public class UploadServlet extends HttpServlet {

	// upload目录,保存上传的资源
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		/*********文件上传组件: 处理文件上传************/
		
		
		try {
			// 1. 文件上传工厂
			FileItemFactory factory = new DiskFileItemFactory();
			// 2. 创建文件上传核心工具类
			ServletFileUpload upload = new ServletFileUpload(factory);
		
			// 一、设置单个文件允许的最大的大小: 30M
			upload.setFileSizeMax(30*1024*1024);
			// 二、设置文件上传表单允许的总大小: 80M
			upload.setSizeMax(80*1024*1024);
			// 三、 设置上传表单文件名的编码
			// 相当于:request.setCharacterEncoding("UTF-8");
			upload.setHeaderEncoding("UTF-8");
			
			
			// 3. 判断: 当前表单是否为文件上传表单
			if (upload.isMultipartContent(request)){
				// 4. 把请求数据转换为一个个FileItem对象,再用集合封装
				List<FileItem> list = upload.parseRequest(request);
				// 遍历: 得到每一个上传的数据
				for (FileItem item: list){
					// 判断:普通文本数据
					if (item.isFormField()){
						// 普通文本数据
						String fieldName = item.getFieldName();	// 表单元素名称
						String content = item.getString();		// 表单元素名称, 对应的数据
						//item.getString("UTF-8");  指定编码
						System.out.println(fieldName + " " + content);
					}
					// 上传文件(文件流) ----> 上传到upload目录下
					else {
						// 普通文本数据
						String fieldName = item.getFieldName();	// 表单元素名称
						String name = item.getName();			// 文件名				
						String content = item.getString();		// 表单元素名称, 对应的数据
						String type = item.getContentType();	// 文件类型
						InputStream in = item.getInputStream(); // 上传文件流
						
						/*
						 *  四、文件名重名
						 *  对于不同用户readme.txt文件,不希望覆盖!
						 *  后台处理: 给用户添加一个唯一标记!
						 */
						// a. 随机生成一个唯一标记
						String id = UUID.randomUUID().toString();
						// b. 与文件名拼接
						name = id +"#"+ name;
						
						// 获取上传基路径
						String path = getServletContext().getRealPath("/upload");
						// 创建目标文件
						File file = new File(path,name);
						
						// 工具类,文件上传
						item.write(file);
						item.delete();   //删除系统产生的临时文件
						
						System.out.println();
					}
					
				}
				
			}
			else {
				System.out.println("当前表单不是文件上传表单,处理失败!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
}

文件上传与下载,完整案例:

步骤:

1. 文件上传

2. 列表下载

Index.jsp

<body>	
  		<a href="${pageContext.request.contextPath }/upload.jsp">文件上传</a> &nbsp;&nbsp;&nbsp;
  		<a href="${pageContext.request.contextPath }/fileServlet?method=downList">文件下载</a> 
  		
  </body>

Upload.jsp

<body>	
  	 <form name="frm_test" action="${pageContext.request.contextPath }/fileServlet?method=upload" method="post" enctype="multipart/form-data">
  	 	 <%--<input type="hidden" name="method" value="upload">--%>
  	 	 
  	 	 用户名:<input type="text" name="userName">  <br/>
  	 	文件:   <input type="file" name="file_img">   <br/>
  	 	
  	 	<input type="submit" value="提交">
   	 </form>
  </body>

Downlist.jsp

<body>	
	<table border="1" align="center">
		<tr>
			<th>序号</th>
			<th>文件名</th>
			<th>操作</th>
		</tr>
		<c:forEach var="en" items="${requestScope.fileNames}" varStatus="vs">
			<tr>
				<td>${vs.count }</td>
				<td>${en.value }</td>
				<td>
					<%--<a href="${pageContext.request.contextPath }/fileServlet?method=down&..">下载</a>--%>
					<!-- 构建一个地址  -->
					<c:url var="url" value="fileServlet">
						<c:param name="method" value="down"></c:param>
						<c:param name="fileName" value="${en.key}"></c:param>
					</c:url>
					<!-- 使用上面地址 -->
					<a href="${url }">下载</a>
				</td>
			</tr>
		</c:forEach>
	</table>  		
  </body>

FileServlet.java


/**
 * 处理文件上传与下载
 * @author Jie.Yuan
 *
 */
public class FileServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// 获取请求参数: 区分不同的操作类型
		String method = request.getParameter("method");
		if ("upload".equals(method)) {
			// 上传
			upload(request,response);
		}
		
		else if ("downList".equals(method)) {
			// 进入下载列表
			downList(request,response);
		}
		
		else if ("down".equals(method)) {
			// 下载
			down(request,response);
		}
	}
	
	
	/**
	 * 1. 上传
	 */
	private void upload(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
		
		try {
			// 1. 创建工厂对象
			FileItemFactory factory = new DiskFileItemFactory();
			// 2. 文件上传核心工具类
			ServletFileUpload upload = new ServletFileUpload(factory);
			// 设置大小限制参数
			upload.setFileSizeMax(10*1024*1024);	// 单个文件大小限制
			upload.setSizeMax(50*1024*1024);		// 总文件大小限制
			upload.setHeaderEncoding("UTF-8");		// 对中文文件编码处理

			// 判断
			if (upload.isMultipartContent(request)) {
				// 3. 把请求数据转换为list集合
				List<FileItem> list = upload.parseRequest(request);
				// 遍历
				for (FileItem item : list){
					// 判断:普通文本数据
					if (item.isFormField()){
						// 获取名称
						String name = item.getFieldName();
						// 获取值
						String value = item.getString();
						System.out.println(value);
					} 
					// 文件表单项
					else {
						/******** 文件上传 ***********/
						// a. 获取文件名称
						String name = item.getName();
						// ----处理上传文件名重名问题----
						// a1. 先得到唯一标记
						String id = UUID.randomUUID().toString();
						// a2. 拼接文件名
						name = id + "#" + name;						
						
						// b. 得到上传目录
						String basePath = getServletContext().getRealPath("/upload");
						// c. 创建要上传的文件对象
						File file = new File(basePath,name);
						// d. 上传
						item.write(file);
						item.delete();  // 删除组件运行时产生的临时文件
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
			
		
	}

	
	/**
	 * 2. 进入下载列表
	 */
	private void downList(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
		
		// 实现思路:先获取upload目录下所有文件的文件名,再保存;跳转到down.jsp列表展示
		
		//1. 初始化map集合Map<包含唯一标记的文件名, 简短文件名>  ;
		Map<String,String> fileNames = new HashMap<String,String>();
		
		//2. 获取上传目录,及其下所有的文件的文件名
		String bathPath = getServletContext().getRealPath("/upload");
		// 目录
		File file = new File(bathPath);
		// 目录下,所有文件名
		String list[] = file.list();
		// 遍历,封装
		if (list != null && list.length > 0){
			for (int i=0; i<list.length; i++){
				// 全名
				String fileName = list[i];
				// 短名
				String shortName = fileName.substring(fileName.lastIndexOf("#")+1);
				// 封装
				fileNames.put(fileName, shortName);
			}
		}
		
		// 3. 保存到request域
		request.setAttribute("fileNames", fileNames);
		// 4. 转发
		request.getRequestDispatcher("/downlist.jsp").forward(request, response);

	}

	
	/**
	 *  3. 处理下载
	 */
	private void down(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
		
		// 获取用户下载的文件名称(url地址后追加数据,get)
		String fileName = request.getParameter("fileName");
		fileName = new String(fileName.getBytes("ISO8859-1"),"UTF-8");
		
		// 先获取上传目录路径
		String basePath = getServletContext().getRealPath("/upload");
		// 获取一个文件流
		InputStream in = new FileInputStream(new File(basePath,fileName));
		
		// 如果文件名是中文,需要进行url编码
		fileName = URLEncoder.encode(fileName, "UTF-8");
		// 设置下载的响应头
		response.setHeader("content-disposition", "attachment;fileName=" + fileName);
		
		// 获取response字节流
		OutputStream out = response.getOutputStream();
		byte[] b = new byte[1024];
		int len = -1;
		while ((len = in.read(b)) != -1){
			out.write(b, 0, len);
		}
		// 关闭
		out.close();
		in.close();
		
		
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doGet(request, response);
	}

}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/u/3299202/blog/3067822

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值