springmvc的学习

  1、mvc 框架要做哪些事情:

       a) 将url映射到java类或java类的方法。

       b) 封装用户提交的数据。

       c) 处理请求,调用相关的业务封装响应的数据。

       d) 将响应数据进行渲染,jsp,html等界面显示。


  以下的内容是观看尚硅谷佟刚老师的视频,所做的笔记。

  2、配置使用delete请求和put请求

      1)首先配置拦截器

       <!-- 配置拦截器, -->
      <filter> 
           <filter-name>hiddenHttpMethodFilter</filter-name>
           <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
      </filter>
      
      <filter-mapping>
         <filter-name>hiddenHttpMethodFilter</filter-name>
         <url-pattern>/*</url-pattern>
      </filter-mapping>

      2)表单中要做以下配置

扫描二维码关注公众号,回复: 1372953 查看本文章

        添加一个隐藏域,指明将该post请求转成delete请求,put请求也一样,只要将delete改成put就行了。

       <form action="springmvc/testMvc5/1" method="POST">
               <input type="hidden" name="_method" value="DELETE">
               <input type="submit" value="submitdelete">
       </form>

       3)对应的controller方法

        注意:我在控制器上加了@RequestMapping("/springmvc")

       @RequestBody    //记得这个也要加,否则可能会出现jsp只支持post,get等方法的错误

       @RequestMapping(value="/testMvc5/{id}",method=RequestMethod.DELETE)

        public String testMvcRestDelete(@PathVariable("id") Integer id) {
     System.out.println("mvc...rest...delete.."+id);
     return SUCCESS;
        }

 3、使用@RequestParam这个注解

     @RequestMapping(value="/testMvc6")
     public String testMvcRequestParam(@RequestParam("username") String username
    ,@RequestParam("uuid") int uuid) {
   System.out.println("usename:"+username+",uuid:"+uuid);
   return SUCCESS;
     }

     请求的URL为:http://localhost:8080/mvchelloworld/springmvc/testMvc6?username=cjh&uuid=33

     控制台的结果为:usename:cjh,uuid:33

     此时如果不传uuid  http://localhost:8080/mvchelloworld/springmvc/testMvc6?username=cjh

    出现以下异常:

HTTP Status 400 – Bad Request


Type Status Report

Message Required int parameter 'uuid' is not present

Description The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).


Apache Tomcat/9.0.4

此时可以在 @RequestParam(value="uuid" ,required=false),添加红色部分,然后运行:买买提的,出现以下错误:
java.lang.IllegalStateException: Optional int parameter 'uuid' is not present but cannot be translated into a null value due to being declared as a primitive type
原来是没有uuid后,因为uuid是int类型的,所以不知道给它指定什么值,此时还可以,加一个属性:
@RequestParam(value="uuid",required=false,defaultValue="0") 然后就可以了。
这里注意了:如果uuid的原类型为Integer这个引用类型的话,可以不加defaultValue的属性,会主动设置为null。

 4、出现以下异常:对应的实体类中没有空构造器。

org.springframework.beans.NullValueInNestedPathException: Invalid property 'deparment' of bean class [com.jieli.entities.Employee]: Could not instantiate property type [com.jieli.entities.Department] to auto-grow nested property path: java.lang.Instantiat

5、自定义数据转换器

1)视图界面:

    <form:form action="myConverter" method="POST">
          Employee:<input type="text" name="employee">
          <input type="submit" value="submit">

    </form:form>

2)controller界面:

    @Controller
    public class EmployeeConverterHandler {
@Autowired
private EmployeeDao employeeDao;
       @RequestMapping("/myConverter")
public String converter(@RequestParam("employee") Employee employee) {
    employeeDao.save(employee);
return "redirect:/emps";
}

    }

3)自定义的转换器类:

     @Component("employeeConverter")
     public class EmployeeConverter implements Converter<String, Employee>{
        //引入部门
@Autowired
private DepartmentDao departmentDao;
@Override
public Employee convert(String arg0) {
//采用-的形式
Employee employee = null;
if(arg0 != null) {
String[] vals = arg0.split("-");
String lastName = vals[0];
String email    = vals[1];
Integer gender  = Integer.parseInt(vals[2]);
Department department = departmentDao.getDepartment(Integer.parseInt(vals[3]));
employee = new Employee(null, lastName, email, gender, department);
}
return employee;

}}

4)mvc中的转换配置:

    <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
    <bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
       <property name="converters">
         <set>
            <ref bean="employeeConverter"/>
         </set>
       </property>

    </bean>

6、使用initBinder取消某个属性的自动绑定。

    1)只要在handler中加上一个方法就可以

        @InitBinder
	public void initBinder(WebDataBinder dataBinder) {
		dataBinder.setDisallowedFields("lastName");
	}
     那么在进行属性的自动赋值的时候,lastName属性不会被赋值。注意该方法不能有返回值。

 7、数据的验证。

 1)加入相应的jar包

 2) 在对应的实体类上加入相应的注解

        @NotEmpty

private String lastName;

        @Past //这里需要一个过去的时间,如果不是的,会打印 birth:需要是一个过去的事件的错误信息
@DateTimeFormat(pattern="yyyy-MM-dd")

 private Date birth;

        注意:如果没有上述的配置,字符串是无法转成相应的日期的,同时会对日期进行相应的限制。

 3) 控制器中打印相应的信息,通过产生的错误,可以直接跳转到对应的界面

        @RequestMapping(value="/emp",method=RequestMethod.POST)
	public String saveEmployee(@Valid Employee employee,Errors result,Map<String, Object> map) {
		if(result.getErrorCount() > 0){
			System.out.println("出错了!");
			for(FieldError error:result.getFieldErrors()){
				System.out.println(error.getField() + ":" + error.getDefaultMessage());
			}
			//若验证出错, 则转向定制的页面
			map.put("departments", departmentDao.getAllDepartments());
			return "input";
		}
		
		employeeDao.save(employee);
		return "redirect:/emps";
	}

4)在mvc的配置文件中加入以下的注解。

 <mvc:annotation-driven></mvc:annotation-driven>

8、数据验证中自定义输入错误信息,通过国际化的形式。

     1)在类路径下加入 i18n.properties 文件,上述7中第四条也得满足。

           里面的编辑内容为: 格式化的注解.实体类在请求域中的名.属性名

            Past.employee.birth=xxxxxx

typeMismatch.employee.birth=xxxxxx(如果不是指定的日期格式的时候,这个将起作用)

      2)再mvc文件中进行以下的配置:

     <bean  id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
         <property name="basename" value="i18n"></property>
     </bean>

      3)在显示的界面进行相关的显示:

     <form:errors path="*"></form:errors>     会打印所有的错误信息
     <form:errors path="birth"></form:errors> 打印指定字段的信息

9、静态资源的拦截问题

     由于下面要记性json数据格式的使用,所以会用到js来操作,这里在导入js静态资源的时候,可能会无法找到

相应的资源,可以有以下两种方式进行拦截的配置。

     1)在mvc配置文件中进行以下的配置

	<!--  
		default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
		它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 
		Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理

		一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
		若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
		
	-->
	<mvc:default-servlet-handler/>

     2)在mvc配置文件中进行以下的配置:

      <!-- 静态资源的访问设置 -->
      <mvc:annotation-driven></mvc:annotation-driven>
      <mvc:resources mapping="/plugins/**" location="/WEB-INF/plugins/" /> 

          凡是请求路径中包含了plugins的请求都会被拦截,到location指定的位置进行查找

10、使用json格式的数据。

          注意:引入静态资源时,记得进行静态资源访问的配置,不然会当做普通请求拦截,从而出现404的错误。

      1)加入jar包,三个:

      jackson-annotations-2.1.5.jar
      jackson-core-2.1.5.jar

      jackson-databind-2.1.5.jar

      加入项目中后,会在messageConverters中加入一个     [6] MappingJackson2HttpMessageConverter

      2)控制器的代码:  

        @ResponseBody
	@RequestMapping("/empjson")
	public Collection<Employee> getJsonData() {
		return employeeDao.getAll();
	}

      3)jsp界面的代码:弹出对应的信息

<script type="text/javascript">
     $(function(){  
	   $("#testJson").click(function(){
		   var url = this.href;
		   $.post(url,function(data){
			   for(var i=0;i<data.length;i++){
				   alert(data[i].lastName);
			   }
		   });
	   });
	   return false;
   })
</script>
<a href="empjson" id="testJson">Test JSON</a>

11、使用@RequestBody

    1)jsp界面中:

<form action="testHttpMessageConverter" method="POST" enctype="multipart/form-data">
	File: <input type="file" name="file"/>
        Desc: <input type="text" name="desc"/>
        <input type="submit" value="Submit"/>   </from>

    2)控制器:

        @ResponseBody
	@RequestMapping("/testHttpMessageConverter")
	public String testHttpMessageConverter(@RequestBody String body){
		System.out.println(body);
		return "helloworld! " + new Date();
	}

    直接将返回的信息打印到客户端,body的值打印到控制台,会将文件内容打印

12、使用ResponseEntity实现下载:

1)控制器:

        @RequestMapping("/testResponseEntity")
	public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
		byte [] body = null;
		ServletContext servletContext = session.getServletContext();
		InputStream in = servletContext.getResourceAsStream("/files/abc.txt");//站点目录下要有相应的文件,不然出现内部错误
		body = new byte[in.available()];
		in.read(body);
		
		HttpHeaders headers = new HttpHeaders();
		headers.add("Content-Disposition", "attachment;filename=abc.txt");
		
		HttpStatus statusCode = HttpStatus.OK;
		
		ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(body, headers, statusCode);
		return response;
	}
2)jsp界面:
       <a href="testResponseEntity">download</a>

13、国际化:

  1) 通过浏览器的切换实现:

   a) 在类路径下加入下面三个文件:

         i18n_en_US.properties

         内容:i18n.user=user
                   i18n.password=password

         i18n_zh_CN.properties

         内容:i18n.user=\u7528\u6237\u540D
                   i18n.password=\u5BC6\u7801

         i18n.properties

         内容:i18n.user=user

                   i18n.password=password

   b)在mvc的配置文件中加入:这个是不同过控制器直接根据视图解析器跳转:
        <mvc:view-controller path="/i18n" view-name="i18n"/>

<mvc:view-controller path="/i18n2" view-name="i18n2"/>

   c)视图解析器的内容:

   <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean> 

    d)两个jsp文件:在WEB-INF/views下

    i18n.jsp 

    内容:

       <fmt:message key="i18n.user"></fmt:message>
	<br><br>
	<a href="i18n2">I18N2 PAGE</a>

    i18n2.jsp

    内容:

        <fmt:message key="i18n.password"></fmt:message>
	<br><br>
	<a href="i18n">I18N PAGE</a>
注意:在两个jsp文件的头部导入fmt标签:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 e) 浏览器切换语言环境。

2)使用浏览器和Locale实现国际化:

这个跟上述的几乎一样:

  a) 在类路径下加入下面三个文件:

         i18n_en_US.properties

         内容:i18n.user=user
                   i18n.password=password

         i18n_zh_CN.properties

         内容:i18n.user=\u7528\u6237\u540D
                   i18n.password=\u5BC6\u7801

         i18n.properties

         内容:i18n.user=user

                   i18n.password=password

 b)在mvc的配置文件中加入:这个是不同过控制器直接根据视图解析器跳转:

<mvc:view-controller path="/i18n2" view-name="i18n2"/>

 c)在控制器中加入:

        @RequestMapping("/i18n")
	public String testI18n(Locale locale) {
		String val = rbms.getMessage("i18n.user", null, locale);
		System.out.println(val);//中文环境下打印用户名;英文环境下打印user
		return "i18n";
	}

d)两个jsp文件:在WEB-INF/views下

    i18n.jsp 

    内容:

       <fmt:message key="i18n.user"></fmt:message>
	<br><br>
	<a href="i18n2">I18N2 PAGE</a>

    i18n2.jsp

    内容:

        <fmt:message key="i18n.password"></fmt:message>
	<br><br>
	<a href="i18n">I18N PAGE</a>
注意:在两个jsp文件的头部导入fmt标签:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

3)通过超链接实现切换语言:

  a)i18n.jsp界面:

        <fmt:message key="i18n.user"></fmt:message>
	<br><br>
	<a href="i18n2">I18N2 PAGE</a>
	
	<br>
	<a href="i18n?locale=zh_CN">中文</a>
	<br>
	<a href="i18n?locale=en_US">英文</a>
 b)在mvc的配置文件中加入两个配置:
        <mvc:view-controller path="/i18n2" view-name="i18n2"/>
	<!--配置locale解析器和拦截器-->
	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
	<mvc:interceptors>
	  <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
	</mvc:interceptors>

c)控制台:

        @RequestMapping("/i18n")
	public String testI18n(Locale locale) {
		String val = rbms.getMessage("i18n.user", null, locale);
		System.out.println(val);
		return "i18n";
	}
d)   在类路径下加入下面三个文件:

         i18n_en_US.properties

         内容:i18n.user=user
                   i18n.password=password

         i18n_zh_CN.properties

         内容:i18n.user=\u7528\u6237\u540D
                   i18n.password=\u5BC6\u7801

         i18n.properties

         内容:i18n.user=user

                   i18n.password=password

 <!-- 配置文件上传的MutipartResolver -->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	          <property name="defaultEncoding" value="UTF-8"></property>
	          <property name="maxUploadSize" value="1024000"></property>
	</bean>

14、文件上传:

1)在springmvc的配置文件中配置MutipartResolver

        <!-- 配置文件上传的MutipartResolver -->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	          <property name="defaultEncoding" value="UTF-8"></property>
	          <property name="maxUploadSize" value="1024000"></property>
	</bean>

2)写一个目标方法,方法中使用MultipartFile参数 来处理文件的信息

        @RequestMapping("/testUpload")
	public String testFileUpload(@RequestParam("desc") String desc,
			@RequestParam("file") MultipartFile file) {
		String fileName = file.getOriginalFilename();
		InputStream in       = null;
		FileOutputStream out = null;
		try {
			 in = file.getInputStream();
			 byte[] b = new byte[1024];
			 int lent = 0;
			 File uploadFile =  new File("C:\\Users\\Administrator\\Desktop\\test/"+fileName);
			 System.out.println(fileName);
			 System.out.println(desc);
			 out = new FileOutputStream(uploadFile);
			 while((lent=in.read(b))!=-1) {
				 out.write(b);
			 }		
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		  return "success";
	    }

3)jsp界面,需要一个form,enctype="multipart/form-data"。

  <form action="testUpload" method="POST" enctype="multipart/form-data">
		  File: <input type="file" name="file"/>
		  Desc: <input type="text" name="desc"/>
		        <input type="submit" value="Submit"/>
 </form>

15、自定义拦截器

1)实现HandlerInterceptor接口,实现三个方法

  public class MyInterceptor implements HandlerInterceptor{
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
            System.out.println("[MyInterceptor] afterCompletion");		
	}

	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	    	System.out.println("[MyInterceptor] postHandle");				
	}

	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		     System.out.println("[MyInterceptor] preHandle");		
		 
		     return true;
	}
    }
preHandler()方法在目标方法之前被执行,如果返回false,那么其他后面的拦截器和目标方法将不被执行
     可以做权限、日志、是事务等。
     
postHandler()方法在调用目标方法之后和渲染视图之前被调用的。
     可以对请求域中的属性和视图做出修改
     
afterCompletion()方法渲染视图之后被调用

    可以释放资源

2)在springmvc文件中进行该拦截器的配置。

         <mvc:interceptors>
	    <!-- 配置自定义拦截器 -->
	    <bean class="com.jieli.interceptors.MyInterceptor"></bean>
	 </mvc:interceptors>

16、拦截器详细的配置:

          <!-- 配置拦截器(不)作用的路径 -->
          <mvc:interceptors>
		   <mvc:interceptor>
		     <!--作用指定的路劲,对其他路径不起作用-->
			 <mvc:mapping path="/emps"/>
			 <bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
		   </mvc:interceptor>
	  </mvc:interceptors>	 

17、对于多个拦截器,方法的调用顺序

   first:preHandler -> second:preHandler->目标handler方法
   ->second:postHandler->first:postHandler->渲染视图render
   ->second:afterCompletion->first:afterCompletion
   

18、异常的处理:

1)ExeceptionHandlerExceptionResolver

 在handler(对应的controller中)类中加入相应的异常处理方法:

    //处理数学的异常。
    @ExceptionHandler({ArithmeticException.class})
	public String catchException(Exception ex) {
		System.out.println(ex);
		return "error";//跳转到error界面
    }
2)如何将异常带到客户端。   注意,不能使用Map参数,否则该异常处理不起作用。

   使用ModelAndView来处理:将可以将异常打印到客户端

        @ExceptionHandler({ArithmeticException.class})
	public ModelAndView catchException(Exception ex) {
		System.out.println(ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception",ex);
		return mv;
	}

3)异常的优先级的问题

加入以下的配置,异常会优先使用精确配置的异常处理,如果找不到

精确匹配的将会使用以下范围比较大的进行匹配。

        @ExceptionHandler({RuntimeException.class})
	public ModelAndView catchException2(Exception ex) {
		System.out.println("RUNTIME"+ex);
		ModelAndView mv = new ModelAndView("error");
		return mv;
	}
4)使用定义的类来处理异常
@ControllerAdvice
  public class HandlerException {
	@ExceptionHandler({ArithmeticException.class})
	public ModelAndView catchException(Exception ex) {
		System.out.println("class->"+ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception",ex);
		return mv;
	}
  }
 如果在当前的handler中,没有找到 @ExceptionHandler来处理相应的异常
      将会在@ControllerAdvice标记的类中找@ExceptionHandler标记的方法来处理。
      注意:handler中的异常处理优先 ,然后才到自定义的类中进行异常的处理

5)异常处理:ResponseStatausExecptionResolver     

 a)定义的异常类:

 @ResponseStatus(reason="用户名和密码不匹配",value=HttpStatus.BAD_REQUEST)
 public class UserNotMatchPwdExeception extends RuntimeException{
	private static final long serialVersionUID = 1L;
 }  

b)handler中的方法:

        @RequestMapping("/exceptionResponse")
	public String testExceptionResponse(@RequestParam("i") Integer i) {
		if(i==3) {
			throw new UserNotMatchPwdExeception();
		}
		System.out.println("testExceptionResponse");
		return "success";
	}

c)当请求路径中带有i=3的时候:

        HTTP Status 400 – Bad Request
Type Status Report

Message 用户名和密码不匹配

6)@ResponseStatus当然也可以定义在handler的方法中:

        @ResponseStatus(value=HttpStatus.FORBIDDEN,reason="定义在方法上")
	@RequestMapping("/exceptionResponse")
	public String testExceptionResponse(@RequestParam("i") Integer i) {
		if(i==3) {
			throw new UserNotMatchPwdExeception();
		}
		System.out.println("testExceptionResponse");
		return "success";
	}	
   方法会正确执行,但是不会转到success的界面,在客户端还是会爆出错误:
   HTTP Status 403 – Forbidden
   Type Status Report

   Message 定义在方法上

7)异常处理:DefaultHandlerExceptionResolver

 处理spring的一些特定的请求的。比如以下例子:

        @RequestMapping(value="/exceptionResponse",method=RequestMethod.POST)
	public String testExceptionResponse(@RequestParam("i") Integer i) {
	
		System.out.println("testExceptionResponse");
		return "success";
	}
 当请求使用:http://localhost:8080/springmvccrud/exceptionResponse?i=4时,会出现:
    HTTP Status 405 – Method Not Allowed
    Type Status Report
    Message Request method 'GET' not supported
    这个异常就是通过DefaultHandlerExceptionResolver处理的

8)SimpleMappingExeceptionResolver

 通过mvc的配置文件进行处理

        <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	     <property name="exceptionMappings">
	       <props>
	         <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
	       </props>
	     </property>
	</bean>
  发现如果这个key不输入值,好像可以匹配很多异常,然后跳转到error的界面,同时会将异常信息带到客户端
       这个变量默认为exeception,放入到了请求域中,当然也有设置该属性名的配置:

  加入一个属性就可以了:

<property name="exceptionAttribute" value="ex"></property>
19、springmvc的运行流程:   

        首先请求被springmvc的DispatcherServlet拦截->然后在springmvc的handler中进行
        映射:
        1、不存相关的映射:
                    1、是否配置了 <mvc:default-servlet-handler/> (静态资源拦截):
                             1、无的话,出现NotMapping的异常。404错误。
                             2、有的话,找静态的目标资源,如果也没有,出现异常。
        2、存在对应的映射:     
                                                   由HandlerMapping获取HandlerExecutionChain对象
                                                            获取获HandlerAdapter对象->调用拦截器的preHandler的方法
                                                                  调用Handler的目标方法得到ModelAndView对象 ->调用拦截器
                                                                      的postHandler的方法,是否存在异常:
                                                                            存在:由HandlerExeceptionResolver组件处理,得到新的ModelAndView
                                                                            不存在:由viewResolver得到实际的view
                                                                                   渲染视图 ->调用拦截器的afterCompletion方法。

20、关于spring和springmvc整合的问题:

1)若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.

比如:

springmvc配置文件中:
<context:component-scan base-package="com.atguigu.springmvc" ></context:component-scan>

spring的配置文件中:

<context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>

解决的方法:

1、使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分. 

2、使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解:

springmvc配置文件中:

        <context:component-scan base-package="com.atguigu.springmvc" use-default-filters="false">
		<context:include-filter type="annotation" 
			expression="org.springframework.stereotype.Controller"/>
		<context:include-filter type="annotation" 
			expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
	</context:component-scan>

spring的配置文件中:

        <context:component-scan base-package="com.atguigu.springmvc">
		<context:exclude-filter type="annotation" 
			expression="org.springframework.stereotype.Controller"/>
		<context:exclude-filter type="annotation" 
			expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
	</context:component-scan>
21、文件的下载:
1)jsp界面:
 <a href="mydownload?file=mysql.txt">mydownload</a>
2)在WebContent的file目录下应该有相应的mysql.txt文件:

                         

3)对应的controller的代码:

        @RequestMapping("mydownload")
	public String myDownload(@RequestParam("file") String file,
			HttpSession session,HttpServletResponse response) {
		response.setHeader("Content-Disposition", "attachment;fileName="+file);
		String filePath = session.getServletContext().getRealPath("file")+"/"+file;
		File   downloadFile = new File(filePath);
		InputStream is      = null;
		OutputStream os     = null;
		int lent            = 0;
		byte[]   b          = new byte[1024];
		try {
			 is = new FileInputStream(downloadFile) ;
			 os = response.getOutputStream();
		     while((lent=is.read(b)) !=-1 ) {
		    	 os.write(b);
		     }
			 
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				is.close();
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
注意返回值应该为null,不然会出现以下的错误(但是还是可以下载文件):

java.lang.IllegalStateException: getOutputStream() has already been called for this response

注意文件名中文乱码问题:

可以通过utf-8先解码,然后再编码:

file = new String(file.getBytes("utf-8"), "ISO-8859-1")
也可以通过:
URLEncoder.encode(file, "UTF-8")
如果没有这一句:
response.setHeader("Content-Disposition", "attachment;fileName="+URLEncoder.encode(file, "UTF-8"))
下载的文件将以请求url的mydownload命名,且没有后缀。






  


猜你喜欢

转载自blog.csdn.net/chenjianhuideyueding/article/details/79154717
今日推荐