Java SpringMVC

前言:学习SpringMVC的笔记

首先来解释什么是MVC?

Model 数据库层的交互

View 视图层的交互

Controller 控制层的交互

Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。

Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。

这种MVC就是跟自己印象中的一样,唯一的不同可能就是结合了spring框架的特点,通过IOC AOP来进行开发的特点。

SpringMVC原生搭建

maven依赖的导入

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.9.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>
</dependencies>

web.xml的配置

解释:这里最重要的就是注册DispatcherServlet类,因为在SpringMVC中最核心的就是DispatcherServlet,当我们配置了该DispatcherServlet类,Spring会通过该DispatcherServlet类来接管分发所有的请求。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--1.注册DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别-1-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

springmvc-servlet.xml的配置

解释:通过BeanNameUrlHandlerMapping,SimpleControllerHandlerAdapter,InternalResourceViewResolver的对象进行注入,spring能够将我们的HTTP请求进行映射,然后执行对应的Controller对象,最后将对应的视图渲染到页面上进行展示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--URL映射相关模块注入到spring中-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!--视图解析器:DispatcherServlet给它的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--Handler-->
    <bean id="/hello" class="com.zpchcbd.controller.HelloController"/>
</beans>

HelloController.java

解释:在SpringMVC的架构中,我们就不需要来编写Servlet对象了,而这里的话是用Controller控制器类来进行代替Servlet的作用

package com.zpchcbd.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//注意:这里我们先导入Controller接口
public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //ModelAndView 模型和视图
        ModelAndView mv = new ModelAndView();
        //封装对象,放在ModelAndView中
        mv.addObject("msg","HelloSpringMVC!");
        //封装要跳转的视图,放在ModelAndView中
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;
    }
}

hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Kuangshen</title>
</head>
<body>
${msg}
</body>
</html>

最后配置tomcat,访问 http://127.0.0.1:8080/ ,可以发现SpringMVC架构搭建成功了。

SpringMVC执行原理剖析

在前面已经搭建了一个SpringMVC的架构的站点,这里来对其执行原理进行剖析来加深印象

需要先知道的一个点,在SpringMVC的架构中,DispatcherServlet类发挥着最核心的作用,对于收到的外界请求来进行转发、展示的操作

直接来分析DispatcherServlet源码,可在DispatcherServlet.java#doDispatch方法中打下断点

接着来访问上面写好的helloController,此时走到如下的断点位置

然后走到这里,这个位置就是获取HandlerExceutionChain对象,也就是mappedHandler对象

跟到其中的getHandler的方法可以发现DispatcherServlet先通过BeanNameUrlHandlerMapping对象调用getHandler方法来获得HandlerExceutionChain对象来进行返回

接着DispatcherServlet通过上面获取的HandlerExceutionChain对象,获取HandlerExceutionChain的handler中的Adapter,也就是HandlerAdapter对象

再接着DispatcherServlet通过HandlerAdapter(SimpleControllerHandlerAdapter)来调用对应的要执行的控制器类

SimpleControllerHandlerAdapter的handle方法来调用控制器

调用对应的控制器类如下

上面通过调用对应的控制器拿到的modelView对象,就会在processDispatchResult方法中作为参数传入,这个方法就会将控制器中的数据和视图来进行展示

processDispatchResult方法中通过viewResolver来获取对应的路径下的jsp文件然后进行返回view对象(携带了数据和要跳转的URL)

总结流程图如下所示:

SpringMVC注解搭建

前面的搭建是通过springMVC原生的写法来进行写的,现在提供了注解的开发方式,这样就更加方便开发,这里就重新写一份,其他的都不需要变化,只需要在web.xml和控制器类中有些变动

这里会多了一些代码:

1、<context:component-scan base-package="com.zpchcbd.controller"/> 这个标签之前是见过的,单纯用注解写的类还不行,还需要告诉spring容器,将用注解写的类注册到spring容器中

2、<mvc:default-servlet-handler /> ,如果将DispatcherServlet请求映射配置为"/",则Spring MVC将捕获Web容器所有的请求,包括静态资源的请求,Spring MVC会将它们当成一个普通请求处理,因此找不到对应处理器将导致错误。

而这个标签的含义则是让spring容器不处理静态资源,比如.html .js .css,这样就使得这个不必要的错误的发生。

在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

3、<mvc:annotation-driven /> ,因为上面分析过springMVC的执行原理,这里因为通过注解开发,所以默认的处理映射器和处理适配器需要改为注解类的映射器和适配器

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.zpchcbd.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!-- annotation-driven标签实现了DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例的注入-->
    <mvc:annotation-driven />

springmvc-servlet.xml

这里可以看到对应的注册控制器的路由不见了,因为这里是通过注解开发,所以后面可以通过注解来对指定控制器的路径进行注册

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.zpchcbd.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!-- annotation-driven标签实现了DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例的注入-->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

HelloController.java 控制器的编写

package com.zpchcbd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String test01(Model model){
        model.addAttribute("msg", "池灵");
        return "hello";
    }
}

测试环境,结果如下

default-servlet-handler

参考文章:https://blog.51cto.com/u_11966318/5374480

这里具体学习下default-servlet-handler的作用是怎么样的,它的作用就是能够正常访问静态资源

首先在applicationContext.xml中进行配置

    <mvc:default-servlet-handler />
    <mvc:annotation-driven />

此时访问static目录下的hello.jpg也能够正常访问,访问/test接口也能正常访问

实现RestFul风格

所谓的RestFul风格,也就是URL为 http://localhost:8080/add/1/2 http://localhost:8080/delete/1/2 这种格式

又比如有些网站访问的URL为 http://localhost:8080/article/111 那么将访问到的地方就是文章目录的111篇的这种形式。

接下来通过在SpringMVC架构中来进行实现RestFul风格的访问形式,需要变动的就是控制器类中编写。

这里出现的新的知识点有 @RequestMapping , @PathVariable

@RequestMapping:注解中的value 和 path 实质上是一样的,都是来设置访问的路径匹配,通过设置@RequestMapping的method方法来实现相同的URL,根据不同的HTTP请求方式来做不同的逻辑处理

@PathVariable:而这里之所以可以通过localhost:8080/add/a/b来实现打印计算结果的原因,通过该注解来实现的接收访问@RequestMapping设置的请求参数,这里需要注意的是参数名需要对应,要不然是接收不到的

HelloController.java

package com.zpchcbd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {
    @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)
    public String test01(@PathVariable int a, @PathVariable int b, Model model){
        int num = a + b;
        model.addAttribute("msg", "add-get: " + num);
        return "test";
    }

    @RequestMapping(path = "/add/{a}/{b}", method = RequestMethod.POST)
    public String test02(@PathVariable int a, @PathVariable int b, Model model){
        int num = a + b;
        model.addAttribute("msg", "add-post: " + num);
        return "test";
    }

    @RequestMapping(path = "/delete/{a}/{b}", method = RequestMethod.POST)
    public String test03(@PathVariable int a, @PathVariable int b, Model model){
        int num = a + b;
        model.addAttribute("msg", "delete-: " + num);
        return "test";
    }
}

关于转发和重定向

我们之前的转发都是通过视图解析器来进行实现的,而在SpringMVC中有无视图解析器都可以进行进行转发和重定向,这里的笔记就来记录下

有视图解析器的情况下进行转发和重定向

package com.zpchcbd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ForwardController {
    // 有视图解析器的转发写法
    @RequestMapping(value = "/forward_test", method = RequestMethod.GET)
    public String test01(Model model){
        model.addAttribute("msg", "test");
        return "test";
    }

    // 有视图解析器的重定向写法
    @RequestMapping(value = "/redirect_test", method = RequestMethod.GET)
    public String test02(Model model){
        model.addAttribute("msg", "test");
        return "redirect:/aaaa";
    }
}

访问 http://localhost:8080/spring04/forward_test

访问 http://localhost:8080/spring04/redirect_test 直接重定向到了 http://localhost:8080/spring04/aaaa?msg=test

无视图解析器的情况下进行转发和重定向

package com.zpchcbd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class NoViewController {
    // 有视图解析器的转发写法
    @RequestMapping(value = "/no_view_forward_test", method = RequestMethod.GET)
    public String test01(Model model){
        model.addAttribute("msg", "test");
        return "forward:/WEB-INF/jsp/test.jsp";
    }

    // 有视图解析器的重定向写法
    @RequestMapping(value = "/no_view_redirect_test", method = RequestMethod.GET)
    public String test02(Model model){
        model.addAttribute("msg", "test");
        return "redirect:/WEB-INF/jsp/test.jsp";
    }
}

访问 http://localhost:8080/spring04/no_view_forward_test

访问 http://localhost:8080/spring04/no_view_redirect_test 重定向到 http://localhost:8080/spring04/no_view_redirect_test

其实可以发现,在没有注册视图解析器的时候,只需要写上forwardredirect 然后跟上根路径名 同样也能实现 转发和重定向的功能

数据不同的接收处理

单个变量的数据传参

@RequestParam注解:指定web上面接收的参数名,如果指定了注解中的值,那么URL中的“name”就不能接收到参数了,只能是“username”了

用@RequestParam注解有什么好处?在开发中,特地指明这个注解有利于开发的时候的观察分析,因为idea中注解都一般显眼,也容易被注意。

@Controller
public class HelloController {
    @RequestMapping(value="/var_test")
    public String test01(@RequestParam("username") String name, Model model){
        model.addAttribute("msg", name);
        return "test";
    }
}

要求提交的表单域和对象的属性名一致,参数使用对象即可。

对象的数据传参

@Controller
public class HelloController {
    @RequestMapping(value="/object_test")
    public String test01(User user, Model model){
        model.addAttribute("msg", user);
        return "test";
    }
}

需要注意的就是:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。

前端JSON格式的展示

这里又有个要学习的注解,也就是@ResponseBody,通过该注解,如果存在视图解析器的时候,在控制器中return的数据直接是原生的数据,而不会走视图解析器,这个就是该注解的作用。

测试代码,这里的代码中加上了@ResponseBody

@Controller
public class HelloController {
    @RequestMapping(value="/var_test")
    @ResponseBody
    public String test01(@RequestParam("username") String name, Model model){
        model.addAttribute("msg", name);
        return "test";
    }
}

访问可以发现,展示的数据就是"test",而不是username接收的数据,这里就可以体现@ResponseBody的作用了。

这里通过Jackson来对类对象进行解析,代码如下,要记得导入maven依赖才可以

@Controller
public class HelloController {
    @RequestMapping(value="/json_test")
    @ResponseBody
    public String test01() throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        //创建一个对象
        User user = new User("池灵", "21", "男");
        return mapper.writeValueAsString(user);
    }
}

结果如下:

但是可以看到,返回到页面上的数据是乱码的,所以这里还需要对数据进行编码操作,乱码的原因就是返回到页面的时候格式化ISO-8859-1,该编码格式是单字符的,所以就导致了中文字符不能显示出来

这里的解决方法是通过设置@RequestMapping注解中的produces来进行解决

@RequestMapping(value="/json_test", produces="application/json;charset=utf-8")

重新访问可以看到乱码的方式已经解决了。

第二种解决方式就是通过springMVC提供的,在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置,这种方法更加的通用,之后就不用每次都进行@RequestMapping中进行设置了。

<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <beanclass="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       </bean>
       <beanclass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <beanclass="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               </bean>
           </property>
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

重新进行访问,同样也是可以的。

SpringMVC字符乱码的处理

SpringMVC中提供了一种类来专门处理乱码的过滤器

<filter>
   <filter-name>encoding</filter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
       <param-name>encoding</param-name>
       <param-value>utf-8</param-value>
   </init-param>
</filter>
<filter-mapping>
   <filter-name>encoding</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

上面的代码在web.xml中进行配置即可。

拦截器实现登录验证

什么是拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

Filter类是Tomcat提供的一种过滤器的写法,而这里的拦截器Interceptor类则是Spring框架提供的类,同样也能实现拦截请求处理的机制。

拦截器实现的原理:通过动态代理来进行实现的具体应用。

拦截器和过滤器的不同点:

过滤器:

  • servlet规范中的一部分,任何java web工程都可以使用

  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器:

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用

  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

实现拦截器

首先需要配置的是在applicationContext.xml中,需要先对指定的拦截器在Spring中进行注册

1、mvc:interceptors标签可以配置多个mvc:interceptor

2、每个mvc:interceptor标签都需要写上该拦截器作用于的URL 和 要注册的拦截器的类

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/admin/*"/>
        <bean class="com.zpchcbd.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

拦截器的代码

public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("======处理前======");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("======处理后======");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("======处理完======");
    }
}

然后在控制器中进行编写测试该拦截器

@Controller
@RequestMapping(value = "/admin")
public class AdminController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String test01(){
        System.out.println("====controller====");
        return "index";
    }
}

我这里进行访问 http://localhost:8080/spring08/admin/login ,控制台输出的数据为如下

到这里就可以总结下,关于HandlerInterceptorAdapter的方法的解释,preHandle是最先调用的,postHandle是控制器调用完之后再调用,afterCompletion是最后的处理。

这里还有个知识点就是,如果想要/login 请求不走的时候,只需要在当前该路由的拦截器中使得preHandle返回会False即可实现。

实现登录验证拦截器

每个学习相关过滤器和拦截器的时候都会写一个简单的登录验证,这里同样也来写一个。

需要:

1、每次登录成功的用户都会返回一个session,并且该session名字中存在标记位,表示该用户是登录的,登录成功的话则跳转到登录成功的界面,否则重定向登录页面

2、登录的页面中提供一个退出的功能。

上面两个功能通过如下完成

@Controller
@RequestMapping(value = "/admin")
public class AdminController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String test01(){
        return "index";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String login(@RequestParam("username") String name, @RequestParam("password") String pass, Model model, HttpSession httpSession){
        if (name != null && pass != null) {
            if (name.equals("admin") && pass.equals("123456")){
                httpSession.setAttribute("loginInfo", name);
                model.addAttribute("loginInfo", name);
                return "admin";
            }
        }
        return "redirect:login";
    }

    @RequestMapping(value = "/manage", method = RequestMethod.GET)
    public String index(Model model, HttpSession httpSession){
        model.addAttribute("loginInfo", httpSession.getAttribute("loginInfo"));
        return "admin";
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpSession httpSession){
        httpSession.removeAttribute("loginInfo");
        return "redirect:login";
    }
}

3、禁止越权的用户直接进行访问。

public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object userInfo = session.getAttribute("userInfo");
        System.out.println(userInfo);
        if (userInfo != null){
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("======处理后======");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("======处理完======");
    }
}

文件上传

因为springMVC的上传处理类中用到了commons-fileupload的包,所以这里还需要导入该依赖才可以进行使用

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

实现的文件上传代码如下

package com.zpchcbd.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.commons.CommonsMultipartFile;


import javax.servlet.http.HttpServletRequest;
import java.io.*;


@Controller
public class FileController {
    @ResponseBody
    @RequestMapping(value = "/uploadFile", produces = "text/html;charset=utf-8")
    public String test01(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest httpServletRequest) throws IOException {
        // 获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        String uploadFileSize = ""+(int)file.getSize();
        // 如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        //上传路径保存设置
        String path = httpServletRequest.getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); //文件输出流
        //读取写出
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "上传文件名: " + uploadFileName + "\t" + "上传文件保存地址: " + realPath + "\t" + "上传文件大小: " + uploadFileSize + "\t";
    }

}

可以发现成功上传文件

需要注意的地方,在applicationContext.xml中进行注册文件处理类CommonsMultipartResolver的时候,id值必须为multipartResolver,否则上传的时候就会报错。

文件下载

package com.zpchcbd.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.commons.CommonsMultipartFile;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;


@Controller
public class FileController {
    @RequestMapping(value = "/downloadFile")
    public String test02(HttpServletResponse response , HttpServletRequest request) throws IOException {
        //要下载的图片地址
        String path = request.getServletContext().getRealPath("/upload");
        String fileName = "11.jpg";

        //1、设置response 响应头
        response.reset(); //设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8"); //字符编码
        response.setContentType("multipart/form-data"); //二进制传输数据
        //设置响应头
        response.setHeader("Content-Disposition", "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path,fileName);
        //2、 读取文件--输入流
        InputStream input = new FileInputStream(file);
        //3、 写出文件--输出流
        OutputStream out = response.getOutputStream();


        byte[] buff =new byte[1024];
        int index=0;
        //4、执行 写出操作
        while((index= input.read(buff))!= -1){
            out.write(buff, 0, index);
            out.flush();
        }
        out.close();
        input.close();
        return null;
    }
}

posted @ 2021-10-27 00:16  zpchcbd  阅读(188)  评论(0)    收藏  举报