第五章 SpringBoot实现Web的常用功能

2024-01-07 19:03:18

学习目标

  • 掌握SpringBoot中MVC功能的定制和扩展

  • 掌握SpringBoot整合Servlet三大组件的实现

  • 掌握SpringBoot文件上传与下载的实现

通常再Web开发中,会涉及静态资源的访问支持,视图解析器的配置,转换器和格式化器的定制,文件上传下载等功能,甚至还需要考虑到与web服务器关联的Servlet相关组件的定制。SpringBoot框架支持整合一些常用web框架,从而实现web开发,并默认支持web开发中的一些通用功能。本章将对SpringBoot实现web开发中涉及的一些常用功能进行详细讲解。

5.1 SpringMVC的整合支持

为了实现并简化web开发,SpringBoot为一些常用的web开发框架提供了整合支持,例如SpringMVC,SpringWebFlux等框架。使用SpringBoot进行web开发时,只需要再项目中引入独赢web开发框架的依赖启动器即可。那么,SpringBoot再整合一些web框架时实现了那些默认自动化配置,同时,怎样进行web功能扩展,本节就以SpringBoot整合SpringMVC框架实现web开发为例进行讲解。

5.1.1 SpringMVC自动配置介绍

再SpringBoot项目中,一旦引入了web依赖启动器spring-boot-starter-web,那么SpringBoot整合SpringMVC框架默认实现的一些xxxAutoConfiguration自动配置类就会自动生效,几乎可以在无任何额外配置的情况下进行web开发。SpringBoot为整合SpringMVC框架实现web开发,主要提供了一i西安自动化配置的功能特性:

  1. 内置了两个视图解析器:ContentNegotiatingViewResolver和BeanNameViewResolver。

  2. 支持静态资源以及WebJars。

  3. 自动注册了转换器和格式化器

  4. 支持http消息转换器

  5. 自动注册了消息代码解析器

  6. 支持静态项目首页index.html

  7. 支持定制应用图标favicon.ico

  8. 自动初始化web数据绑定器ConfigurableWebBindingInitializer

SpringBoot整合SpringMVC进行web开发时提供了很多默认配置,而且大多数时候使用默认配置即可满足开发需求。例如,SpringBoot整合SpringMVC进行web开发时,不需要额外配置视图解析器。

5.1.2 SpringMVC功能扩展实现

SpringBoot整合SpringMVC进行web开发时提供了很多的自动化配置,但在实际开发中还需要开发者对一些功能进行扩展实现。下面我们通过一个具体的案例讲解SpringBoot整合SpringMVC框架实现web开发的扩展功能。

  1. 项目基础环境搭建

    使用SpringInitializr方式创建项目,并在依赖选择中选择web模块下的web依赖启动器和TemplateEngines模块下的Thymeleaf依赖启动器

为了更直观快速的通过页面查看整合效果,我们将上一个项目中的页面和配置信息都拷贝过来,并通过访问请求地址http://localhost:8080/toLoginPage 访问登录页面。

2.功能扩展实现

接下俩使用SpringBoot整合SpringMVC进行web开发,实现简单的页面跳转功能,这里我们将使用SpringBoot提供的WebMvcConfigurer接口编写自定义配置,并对web功能进行适当扩展。

注册视图管理器

在项目的config包中创建一个实现WebMvcConfigurer接口的配置类,用于对mvc框架功能进行扩展

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * 实现WebMvcConfigurer接口,扩展MVC功能
 */
@Configuration
public class MyMVCconfig implements WebMvcConfigurer {

    // 添加视图管理
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 请求toLoginPage映射路径或者login.html页面都会自动映射到login.html页面
        registry.addViewController("/toLoginPage").setViewName("login");
        registry.addViewController("/login.html").setViewName("login");
    }



}

接下来需要将控制器代码进行注释掉,再重新运行项目,可以实现通过浏览器地址访问/toLoginPage或者/login.html地址都能跳转到login.html页面

通过访问可以看到可以正常访问页面,但是页面上的年份却获取不到,那是因为年份原本是通过控制器经过参数传递给页面的,但是我们现在的请求并没有通过控制器,因此年份这个参数获取不到。因此以上这种请求方式只适合较为简单的无参数视图get方式的请求跳转,对于有参数或需要业务处理的跳转需求,最好还是采用控制器的方式处理请求。

注册自定义拦截器

WebMvcConfigurer接口提供了许多MVC开发相关方法,例如,添加拦截器方法addInterceptors(),添加格式化器方法addFormatters()等。接下来我们使用WebMvcConfigurer接口中的addInterceptors()方法注册自定义拦截器。

在项目的config包中创建一个自定义拦截器类,并编写拦截业务代码

package com.example.demo.config;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Calendar;

/**
 * 自定义一个拦截器类
 */
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 用户请求/admin开头路径时,判断用户是否登录
        String uri = request.getRequestURI();
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (uri.startsWith("/admin") && null == loginUser) {
            response.sendRedirect("/toLoginPage");
            return false;
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        request.setAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse
            response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

然后在MyMVCconfig类中重写addInterceptors()方法注册自定义的拦截器

@Autowired
    private MyInterceptor myInterceptor;
    // 添加拦截器管理
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**")   //拦截所有请求
                .excludePathPatterns("/login.html"); //对于/login.html请求地址进行放行,不进行拦截
    }

将项目进行重启运行,当我们访问/admin地址的时候,会被拦截器进行拦截,拦截器通过重定向修改请求地址访问/toLoginPage,并在跳转请求的同时调用了postHandle()方法,将年份参数发送给页面

当我们访问请求地址是/login.html的时候,拦截器没有拦截,而是允许访问,则会直接进入MyMVCconfig的addViewControllers方法,进行访问网页,那这时候并没有获得年份参数数据,因此这个请求页面上是不会显示年份数据信息。

5.2 SpringBoot整合Servlet三大组件

进行Servlet开发时,首先自定义Servlet、Filter、Listener三大组件,然后再文件web.xml中进行配置,而SpringBoot使用的时内嵌式Servlet容器,没有提供外部配置文件web.xml,那么SpringBoot时如何整合Servlet的相关组件呢?SpringBoot提供了组件注册和路径扫描两种方式整合Servlet三大组件,接下来我们分别对这两种整合方式进行详细讲解。

5.2.1 组件注册整合Servlet三大组件

再SpringBoot中,使用组件注册方式整合内嵌Servlet容器的Servlet、Filter、Listener三大组件时,只需将这些自定义组件通过ServletRegistrationBean,FilterRegistrationBean、ServletListenerRegistrationBean类注册到容器中即可。

  1. 使用组件注册方式整合Servlet

    创建自定义Servlet类。在项目中创建servletComponent包,在该包下创建继承至HttpServlet的类MyServlet

package com.example.demo.servletComponent;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 自定义Servlet类
 */
//@WebServlet("/annotationServlet")
@Component
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.getWriter().write("hello MyServlet");
    }
}

使用@Component注解将MyServlet类作为组件注入Spring容器。MyServlet类继承至HttpServlet,通过HttpServletResponse对象向页面输出“hello MyServlet”。

创建Servlet组件配置类。在项目config包下创建一个Servlet组件配置类ServletConfig,用来对Servlet相关组件进行注册。

package com.example.demo.config;
import com.example.demo.servletComponent.MyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

/**
 * 嵌入式Servlet容器三大组件配置
 */
@Configuration
public class ServletConfig {
    // 注册Servlet组件
    @Bean
    public ServletRegistrationBean getServlet(MyServlet myServlet){
        ServletRegistrationBean registrationBean =
                new ServletRegistrationBean(myServlet,"/myServlet");
        return registrationBean;
    }

}

使用@Configuration注解将ServletConfig标注为配置类,ServletConfig类内部的getServlet()方法用于注册自定义的MyServlet,并返回ServletRegistrationBean对象。启动项目测试,在浏览器上访问/myServlet请求地址,浏览器上能显示MyServlet类返回的文本信息。

2.使用组件注册方式整合Filter

创建自定义Filter类,在servletComponent包下创建一个类MyFilter

package com.example.demo.servletComponent;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
/**
 *  自定义Filter类
 */
//@WebFilter(value = {"/antionLogin","/antionMyFilter"})
@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("hello MyFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {

    }
}

使用@Component注解将当前MyFilter类作为组件注入到Spring容器中。MyFilter类实现了Filter接口,并重写了init(),doFilter()和destroy()方法,在doFilter()方法中向控制台打印了"hello MyFilter"字符串。

向Servlet组件配置类注册自定义Filter类。打开之前创建的ServletConfig,将该自定义Filter类使用组件注册方式进行注册

// 注册Filter组件
@Bean
public FilterRegistrationBean getFilter(MyFilter filter){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
    registrationBean.setUrlPatterns(Arrays.asList("/toLoginPage","/myFilter"));
    return registrationBean;
}

上述代码中,使用组件注册方式注册自定义的MyFilter类。在getFilter(MyFilter filter)方法中,使用setUrlPatterns(Arrays.asList("/toLoginPage","/myFilter"))方法定义了过滤的请求为/toLoginPage和/myFilter,同时使用@Bean注解将当前组装好的FilterRegistrationBean对象作为Bean组件返回。接下来可以运行项目访问以上两个请求地址,因为过滤器中并没有对以上两个请求做处理,因此浏览器上会报404错误,这里不用关注,我们只需要观察控制台是否有打印过滤器中的输出信息即可。

3.使用组件注册方式整合Listener

创建自定义Listener类,在servletComponent包下创建一个类MyListener。

package com.example.demo.servletComponent;

import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 *  自定义Listener类
 */
@Component
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ...");
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");
    }
}

使用@Component注解将MyListener类作为组件注册到Spring容器中。MyListener类实现了ServletContextListener接口,并重写了contextInitialized()和contextDestroyed()方法。需要说明的是,Servlet容器提供了很多Listener接口,我们在自定义的时候根据项目的需求实现对应的接口即可。

向Servlet组件配置类注册自定义Listener类。打开之前创建的ServletConfig,将自定义的MyListener注册。

//注册Listener组件
@Bean
public ServletListenerRegistrationBean getServletListener(MyListener myListener){
    ServletListenerRegistrationBean registrationBean=new ServletListenerRegistrationBean(myListener);
    return registrationBean;
}

接下来启动项目,可以在控制台中查看到监听器初始化的文字显示,"contextInitialized ...",再点击退出按钮的时候,控制台会显示contextDestroyed ...

注意,如果直接点击红色按钮停止项目服务器,则不会打印,项目直接被强制关闭了。

5.2.2 路径扫描整合Servlet三大组件

在SpringBoot中,使用路径扫描的方式整合内嵌式Servlet容器的Servlet,Filter,Listener三大组件时,首先需要在自定义组件上分别添加@WebServlet,@WebFilter和@WebListener注解进行声明,并配置相关注解属性,然后再仙姑主程序启动类上使用@ServletComponentScan注解开启组件扫描即可。

在使用注解方式注册三大组件之前,我们需要将之前创建的ServletConfig类全部注释掉,避免互相干扰。并且需要将自定义的三大组件中的@Component注解进行注释换成@WebServlet,@WebFilter和@WebListener。

package com.example.demo.servletComponent;

import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 自定义Servlet类
 */
@WebServlet("/annotationServlet")
//@Component
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.getWriter().write("hello MyServlet");
    }
}

运行项目,在浏览器上请求访问"/annotationServlet"地址的时候,可以在浏览器上显示出"hello MyServlet"

package com.example.demo.servletComponent;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
 *  自定义Filter类
 */
@WebFilter(value = {"/antionLogin","/antionMyFilter"})
//@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("hello MyFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {

    }
}

重新运行项目,会发现浏览器上请求访问"/antionLogin","/antionMyFilter"这两个地址的时候会被过滤器拦截,并在控制台打印出"hello MyFilter"。

package com.example.demo.servletComponent;

import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 *  自定义Listener类
 */
@WebListener
//@Component
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ...");
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");
    }
}

重新运行项目,效果和注册监听器的方式的结果是一样的。

5.3 文件上传与下载

5.3.1 文件上传

开发Web应用时,文件上传时很常见的一个需求,浏览器通过表单形式将文件以流的形式传递给服务器,服务器再对上传的数据解析处理。虾米那我通过一个案例讲解如何使用SpringBoot实现文件上传。

  1. 编写文件上传表单页面

    在项目的templates模板引擎文件夹下创建一个用来上传文件的upload.html模板页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>动态添加文件上传列表</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <script th:src="@{/login/js/jquery.min.js}"></script>
</head>
<body>
<div th:if="${uploadStatus}" style="color: red" th:text="${uploadStatus}">上传成功</div>
<form th:action="@{/uploadFile}" method="post" enctype="multipart/form-data">
    上传文件:&nbsp;&nbsp;<input type="button" value="添加文件" onclick="add()"/>
    <div id="file" style="margin-top: 10px;" th:value="文件上传区域">  </div>
    <input id="submit" type="submit" value="上传"
           style="display: none;margin-top: 10px;"/>
</form>
<script type="text/javascript">
    // 动态添加上传按钮
    function add(){
        var innerdiv = "<div>";
        innerdiv += "<input type='file' name='fileUpload' required='required'>" +
            "<input type='button' value='删除' onclick='remove(this)'>";
        innerdiv +="</div>";
        $("#file").append(innerdiv);
        // 打开上传按钮
        $("#submit").css("display","block");
    }
    // 删除当前行<div>
    function remove(obj) {
        $(obj).parent().remove();
        if($("#file div").length ==0){
            $("#submit").css("display","none");
        }
    }
</script>
</body>
</html>

2.在全局配置文件中添加文件上传的相关配置

在全局配置文件application.properties中添加文件上传的相关设置

# 单个上传文件大小限制(默认为1MB)
spring.servlet.multipart.max-file-size=10MB
# 总上传文件大小限制(默认为10MB)
spring.servlet.multipart.max-request-size=50MB

如果上传文件超出了限制,则会报错。

3.进行文件上传处理,实现文件上传功能

在controller包下创建一个管理上传下载的控制类FileController,用于实现文件上传功能

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.UUID;
/**
 * 文件管理控制类
 */
@Controller
public class FileController {
    // 向文件上传页面跳转
    @GetMapping("/toUpload")
    public String toUpload(){
        return "upload";
    }
    // 文件上传管理
    @PostMapping("/uploadFile")
    public String uploadFile(MultipartFile[] fileUpload, Model model) {
        // 默认文件上传成功,并返回状态信息
        model.addAttribute("uploadStatus", "上传成功!");
        for (MultipartFile file : fileUpload) {
            // 获取文件名以及后缀名
            String fileName = file.getOriginalFilename();
            // 重新生成文件名(根据具体情况生成对应文件名)
            fileName = UUID.randomUUID()+"_"+fileName;
            // 指定上传文件本地存储目录,不存在需要提前创建
            String dirPath = "E:/file/";
            File filePath = new File(dirPath);
            if(!filePath.exists()){
                filePath.mkdirs();
            }
            try {
                file.transferTo(new File(dirPath+fileName));
            } catch (Exception e) {
                e.printStackTrace();
                // 上传失败,返回失败信息
                model.addAttribute("uploadStatus","上传失败: "+e.getMessage());
            }
        }
        // 携带上传状态信息回调到文件上传页面
        return "upload";
    }
}

?toUpload()方法用于处理路径为“/toUpload”的GET请求,并返回上传页面的路径。uploadFile()方法用于处理路径为“/uploadFile”的POST请求,如果文件上传成功,则会将上传的文件重命名并存储在"E:/file/"目录。如果上传失败,则会提示上传失败的相关信息。需要注意的是,uploadFile()方法的参数fileUpload的名称必须与上传页面中的input的name值一致。

4.效果测试

启动项目,项目启动成功后,在浏览器上访问“http://localhost:8080/toUpload

单击添加文件的按钮,能够动态添加多个文件

?

成功了

添加文件进行上传

5.3.2 文件下载

下载文件能够通过IO流实现,所以多数框架并没有对文件下载进行封装处理。文件下载时涉及不同浏览器的解析处理,可能会出现中文乱码的情况。接下来我们分别针对下载英文名文件和中文名文件进行讲解。

  1. 英文名文件下载

    添加文件下载工具依赖。再pom.xml文件中引入文件下载的一个工具依赖commons-io

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

定制文件下载页面,再项目的templates文件夹下创建一个演示文件下载的download.html模板页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>
<div style="margin-bottom: 10px">文件下载列表:</div>
<table>
    <tr>
        <td>bloglogo.jpg</td>
        <td><a th:href="@{/download(filename='bloglogo.jpg')}">下载文件</a></td>
    </tr>
    <tr>
        <td>Spring Boot应用级开发教程.pdf</td>
        <td><a th:href="@{/download(filename='Spring Boot应用级开发教程.pdf')}">
            下载文件</a></td>
    </tr>
</table>
</body>
</html>

需要注意的是,在文件下载之前,需要保证在文件下载目录(本示例中的E:/file/)中存在文件bbb.jpg和计算机文化与组装.pptx(这只是两个测试文件而已,读者演示时可以自行存放,只要保持文件名统一即可)

编写文件下载处理方法,在之前创建的文件管理控制类FileController中编写文件下载的处理方法

// 向文件下载页面跳转
@GetMapping("/toDownload")
public String toDownload(){
    return "download";
}
// 文件下载管理
@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(String filename){
    // 指定要下载的文件根路径
    String dirPath = "F:/file/";
    // 创建该文件对象
    File file = new File(dirPath + File.separator + filename);
    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    // 通知浏览器以下载方式打开
    headers.setContentDispositionFormData("attachment",filename);
    // 定义以流的形式下载返回文件数据
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    try {
        return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
    } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<byte[]>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
    }
}

上述代码中,toDownload()方法用来处理“/toDownload”的Get请求,并跳转到download.html页面,fileDownload()方法用来处理"/download"的Get请求并进行文件下载处理,下载的数据类型是ResponseEntity<byte[]>

效果测试,实现上述文件下载功能后,启动项目,项目启动成功后,在浏览器上访问(http://localhost:8080/toDownload)进入下载页面,这里先下载英文文件名的文件,点击下载文件,以IE浏览器为例

2.中文名文件下载

在上一步所示的文件下载页面中,单击第2个中文名文件“计算机文化与组装.pptx”后面的下载文件进行下载,对文件名进行下载时,虽然可以成功下载,但是下载后的文件中文名称统一变成了“_”,这显然时不理想的,因此还需要对中文名文件下载进行额外处理,在FileController类的fileDownload()方法中添加处理中文编码的代码,修改后的代码如下

// 所有类型文件下载管理
    @GetMapping("/download")
    public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,
                                               String filename) throws Exception{
        // 指定要下载的文件根路径
        String dirPath = "E:/file/";
        // 创建该文件对象
        File file = new File(dirPath + File.separator + filename);
        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        // 通知浏览器以下载方式打开(下载前对文件名进行转码)
        filename=getFilename(request,filename);
        headers.setContentDispositionFormData("attachment",filename);
        // 定义以流的形式下载返回文件数据
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<byte[]>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
        }
    }
//     根据浏览器的不同进行编码设置,返回编码后的文件名
    private String getFilename(HttpServletRequest request, String filename)
            throws Exception {
        // IE不同版本User-Agent中出现的关键词
        String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
        // 获取请求头代理信息
        String userAgent = request.getHeader("User-Agent");
        for (String keyWord : IEBrowserKeyWords) {
            if (userAgent.contains(keyWord)) {
                //IE内核浏览器,统一为UTF-8编码显示,并对转换的+进行更正
                return URLEncoder.encode(filename, "UTF-8").replace("+"," ");
            }
        }
        //火狐等其它浏览器统一为ISO-8859-1编码显示
        return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
    }

上述代码中,getFilename()方法用来根据不同浏览器对下载的中文名进行转码。其中“User-Agent”用于获取用户下载文件的浏览器内核信息,不同版本的IE浏览器内核可能不同,需要特别查看,如果内核信息时IE则转码为UTF-8,其他浏览器转码为ISO-8859-1即可。

本章小结

本章主要讲解了SpringBoot框架整合SpringMVC实现web开发过程中的一些功能,包括有MVC功能扩展和定制,Servlet三大组件定制,文件上传与下载以及SpringBoot项目的打包和部署。学习完本章后,要充分掌握SpringBoot进行web开发中主要功能的一些配置和扩展,能够完成实际开发中SpringBoot项目的开发和部署工作。

习题

一、填空题

  1. SpringBoot项目中定制SpringMVC的扩展功能,需要提供实现()接口的配置类。

  2. WebMvcConfigurer接口中的()方法可以定制视图管理。

  3. WebMvcConfigurer接口中的()方法可以定制自定义的拦截器。

  4. SpringBoot中使用路径扫描方式整合Servlet组件时,需要用()注解开启组件扫描。

  5. SpringBoot整合SpringMVC实现文件上传时,默认单个文件上传大小限制为()。

二、判断题

  1. SpringBoot为整合SpringMVC实现web开发提供了欢迎页index.html支持。()

  2. SpringBoot中实现SpringMVC的扩展功能,要提供实现WebMvcConfigurer接口的配置类,并开启@EnableWebMvc注解。()

  3. SpringBoot中整合Servlet的Listener组件时,在自定义Listener上添加@Component即可生效。()

  4. SpringBoot整合SpringMVC实现中文名文件下载时,针对IE内核浏览器需要转码为UTF-8。()

  5. SpringBoot提供的打包插件spring-boot-maven-plugin可以将项目打成Jar包和War包。()

三、选择题

  1. SpringBoot为整合SpringMVC实现web开发,提供的功能特性不包括()

    A.配置视图解析器

    B.对WebJars的支持

    C.对拦截器的自动配置

    D.对HttpMessageConverters消息转换器的支持

  2. SpringBoot整合Servlet组件涉及的注册Bean组件有()。(多选)

    A.ServletRegistrationBean

    B.InterceptorRegistrationBean

    C.FilterRegistrationBean

    D.ServletListenerRegistrationBean

  3. SpringBoot中使用路径扫描的方式整合内嵌式Servlet组件时,需要使用的注解有()。(多选)

    A.@WebFilter

    B.@ServletComponentScan

    C.@WebListener

    D.@WebInterceptor

  4. 下列关于SpringBoot整合SpringMVC实现文件上传及下载的说法中,正确的是()

    A.必须使用spring.servlet.multipart.max-file-size来设置单个上传文件的大小限制

    B.处理上传文件方法中,可以使用List<MultipartFile>类型的参数来接收处理单个或多个上传文件

    C.文件上传存储目录“F:/file/”需要提前创建好

    D.对中文文件进行下载时,如果没有进行中文转换,下载的中文文件内容会出现乱码

  5. 下列关于SpringBoot项目War包方式打包部署的说法中,错误的是()

    A.必须使用<packaging>标签将SpringBoot项目默认的Jar包方式修改为War

    B.需要将spring-boot-starter-tomcat使用<scope>provided</scope>声明为已提供provided

    C.必须让主程序启动类继承SpringBootServletInitializer类并实现cinfigure()方法

    D.以War包方式部署项目进行访问,必须在访问路径上添加打包后的项目

文章来源:https://blog.csdn.net/u010321564/article/details/135431711
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。