3. SpringBoot中文件下载
将可以被下载资源放在磁盘的 D:\springbootcodes\springboot_day6\download 路径
这里我们使用jsp开发
引入依赖使tomcat可以解析jsp,设置SpringBoot可以访问jsp资源
<!--使tomcat可以解析jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
用到的包结构
开发jsp下载页面
<%@ page pageEncoding="UTF-8" contentType="text/html; UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>测试文件下载</h1>
<a href="${pageContext.request.contextPath}/file/download?fileName=HELP.md">HELP.md</a>
<a href="${pageContext.request.contextPath}/file/download?fileName=readme.txt">readme.txt</a>
<a href="${pageContext.request.contextPath}/file/download?fileName=项目介绍.md">项目介绍.md</a>
</body>
</html>
application.yml配置文件
server:
port: 8989
servlet:
context-path: /springboot_day6
jsp:
init-parameters:
development: true # 打开jsp开发模式
# 配置jsp模板视图
spring:
mvc:
view:
prefix: /
suffix: .jsp
file:
download:
dir: D:\springbootcodes\springboot_day6\download # 指定下载目录,测试环境
# dir: /home/download 生产环境
# 调整日志的级别
logging:
level:
root: info
com.baizhi: debug
开发Controller中的下载方法
一个请求对应一个响应,响应文件对应一个响应,如果最后跳转的话跳转也对应一个响应,因为我们必须响应文件,所以后端的下载方法不跳转,即没有返回值,返回为void。
package com.baizhi.controller;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URLEncoder;
@Controller
@RequestMapping("file")
public class FileController {
private static final Logger log = LoggerFactory.getLogger(FileController.class);
@Value("${file.download.dir}")
private String realName;
/**
* 测试文件下载
* @param fileName
*/
@RequestMapping("download")
public void download(String fileName, HttpServletResponse response) throws IOException {
log.debug("当前下载文件名为: " + fileName);
log.debug("当前下载文件目录: " + realName);
// 1. 去指定目录中读取文件
File file = new File(realName, fileName);
// 2. 将文件读取为文件输入流
FileInputStream is = new FileInputStream(file);
// 2.5 获取响应流之前,一定要设置以附件形式下载 attachment 附件 inline 在线打开(默认值),在之后加上设置的文件名字
// 这里fileName大小写无所谓,因为文件名可能有中文,所以我们要进行编码
response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
// 3. 获取响应输出流
ServletOutputStream os = response.getOutputStream();
// 4. 输入流复制给输出流
IOUtils.copy(is, os);
// 5. 释放资源
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
// 最后补充:FileCopyUtils.copy(is, os)既可以将输入流复制给输出流,还可以在复制之后关闭资源,可以替代上面的4,5步
}
}
4. SpringBoot中拦截器
SpringBoot集成了了Spring和SpringMVC的优势,SpringBoot沿用了SpringMVC中的拦截器,几乎没有做什么修改,只是在配置拦截器不同。自定义拦截器需要实现系统定义的HandlerInterceptor接口,在SpringMVC中实现HandlerInterceptor接口需要重写preHandle、postHandle、afterCompletion三个方法,而SpringBoot对于HandlerInterceptor默认实现,不是所有的项目三个方法都需要用到,基于自己的业务去选择,这三个方法都是用户可选实现,我们甚至可以不实现里面的任何一种方法,因为它们在接口里都有默认实现,默认什么都不做。
关于自定义拦截器中三个方法的参数细节以及三个方法和controller方法的执行顺序建议阅读我之前总结的SpringMVC中的拦截器,和SpringBoot中拦截器的定义几乎是一样的,只是在配置时不同。
接下来我们来开发一个拦截器
这次我们仍然需要使用jsp,所以我们需要导入相关依赖并设置SpringBoot可以访问jsp资源
引入依赖使tomcat可以解析jsp,设置SpringBoot可以访问jsp资源
<!--使tomcat可以解析jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
需要用到的包结构
开发拦截器
自定义拦截器需要实现HandlerInterceptor接口,实现里面的preHandle、postHandle、afterCompletion方法,不过SpringBoot已经帮我们默认实现了,默认实现为空,我们可以根据我们的需要去实现我们需要的方法,甚至三个方法都不实现。三个方法和controller中方法执行顺序为:
preHandle --> controller中方法 --> postHandle --> afterCompletion方法
只有preHandle 方法返回true放行才会去执行之后的controller中的方法,当访问失败postHandle 不会执行,但是无论如何afterCompletion方法一定会执行,它相当于finally{}代码块
拦截器需要专门放在interceptors包里,这个包是专门用来存放拦截器的
/**
* 自定义拦截器1
*/
public class MyInterceptor1 implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyInterceptor1.class);
//preHanle 最先执行
//参数3: handler 当前请求的控制器方法对象
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("============1==============");
//response.sendRedirect(request.getContextPath() + "/403.jsp");
return true; // 放行 false 中断
}
//参数3: handler 当前请求的控制器方法对象
//参数4: modelAndView 模型和视图 当前请求访问方法的modeandview对象
//正确的时候才会执行这个方法,错误的时候不会执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("============2==============");
//log.debug("view: {}", modelAndView.getViewName());
}
//参数3: handler 当前请求的控制器方法对象
//参数4: ex 如果控制器出现异常时的异常对象
//这个方法: 相当于finally{}代码块 总是执行 无论请求正确,出现异常
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("============3==============");
}
}
开发controller
@Controller
@RequestMapping("demo")
public class DemoController {
private static final Logger log = LoggerFactory.getLogger(DemoController.class);
@RequestMapping("demo")
public String demo(){
log.debug("demo ok!!!");
return "demo"; // 底层会封装成ModelAndView
}
}
配置拦截器
配置拦截器的类需要专门放在config包里,config包是专门用来存放配置类的
@Configuration
public class MvcConfig implements WebMvcConfigurer {
/**
* 配置拦截器相关方法
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 配置拦截器1
registry.addInterceptor(new MyInterceptor1()) // 指定拦截器
.addPathPatterns("/**") //拦截所有controller请求 // .addPathPatterns("/login", "/user")只拦截login和user
.excludePathPatterns("/file/**") // 排除哪些路径
.order(1); //指定拦截器的执行顺序,int 类型.默认按照自然排序(从小到大)的顺序执行,数字相同按照从上往下执行
}
}
测试
最后补充的是如果有多个拦截器那么多个拦截器之间的执行顺序是什么?
多个拦截器的执行顺序
我们创建两个拦截器MyInteceptor1、MyInterceptor2,
在配置类里面配置一下拦截器
测试
5. SpringBoot中war包部署
SpringBoot部署方式有两种,一种是war,一种是jar,我们先来学习如何部署war
- 指定部署方式为war
- 去除SpringBoot内嵌的tomcat
- 在插件中指定入口类
- 配置入口类
- 打包
- 测试
- 指定部署方式为war
SpringBoot在部署的时候默认部署为jar包部署,所以我们需要设置部署方式为war,怎么设置呢,在配置文件加上下面这句话,这个方法写在项目名的下面
<packaging>war</packaging>
- 去除SpringBoot内嵌的tomcat
项目内嵌服务器,外部又有一个服务器,就会有问题,所以要把内嵌的tomcat依赖删除掉,这样就没有内嵌的tomcat了,只有外部一个容器,就能正常运行了。在删除依赖时,我们不仅要删除内嵌的tomcat依赖,如果我们还引入了解析jsp的依赖,我们也要删除掉,因为外部的tomcat默认可以解析jsp
虽然spring-boot-starter-web已经引入了tomcat依赖,但是我们还引入tomcat依赖并设置scope为provide表示只有当前环境可用,打包时内嵌的tomcat不参与打包。
<!---使得内嵌的tomcat-不参与打包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!--provided当前idea环境可用,打包不参与打包-->
<scope>provided</scope>
</dependency>
如果我们还引入了解析jsp的依赖我们也要去除,因为外部的tomcat默认可以解析jsp,我们只需要在jsp依赖里加上provided就可以使解析jsp的依赖在打包时不参与打包
注意:一旦我们不使用内部的tomcat,则application.yml中的项目名和端口号会失效,使用外部tomcat时默认项目名为war包的名字(无.war后缀),端口号默认为8080
- 在插件中指定入口类
把内嵌的tomcat排除,用外部的tomcat 启动,外部的tomcat和SpringBoot没有一点关系,所以需要明确告诉外部的tomcat在启动的时候要执行项目中的哪个入口类去启动,在插件中配置入口类,修改插件,在pom.xml依赖中的插件中加入下面这句话就可以指定入口类
<!--配置springboot的入口类-->
<configuration>
<fork>true</fork>
<!--增加jvm参数-->
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<!--指定入口类-->
<!--根据自己项目情况设定-->
<mainClass>com.baizhi.SpringbootDay6Application</mainClass>
</configuration>
- 配置入口类
我们需要让外部服务器找到入口类的时候明确的在入口类里面告诉它现在用的不是内嵌的服务器了,用的是外部的服务器,让入口类继承SpringBootServletInitializer,SpringBootServletInitializer的作用就是指定不再使用内嵌的服务器,而是使用外部tomcat容器启动,还需要覆盖SpringBootServletInitializer中的configure方法。
// SpringBootServletInitializer: 不再使用内嵌的服务器启动,而是使用外部的tomcat容器(服务器)启动
@SpringBootApplication
public class SpringbootDay6Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringbootDay6Application.class, args);
}
// 明确再配置一遍当前的入口类是谁
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootDay6Application.class); // 把入口类的class传进去
}
}
- 打包
注:打包时打包的是springboot_day6,而不是springboot_day5,下图演示错了
我们将打包成的war包名字修改为springboot
- 测试
将war包复制到tomcat里的webapps包里
双击tomcat中bin包下的start.bat运行Tomcat
访问
6. SpringBoot中Jar包部署
- 设置打包方式为jar
在pom.xml中设置打包方式为jar,不过不设置也可以,默认就是jar包方式部署
<!--指定jar包部署-->
<packaging>jar</packaging>
- 执行打包
- 启动jar包
- 注意事项
在使用jar包部署时如果访问的是jsp,需要设置插件为1.4.2版本
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--打包jsp模板时,必须使用1.4.2插件-->
<version>1.4.2.RELEASE</version>
</plugin>
还需要指定jsp文件打包位置,把项目中的哪个jsp打到jar包的哪个目录
<!--指定jsp文件打包位置-->
<resources>
<!-- 打包时将jsp文件拷贝到META-INF目录下-->
<resource>
<!-- 指定resources插件处理哪个目录下的资源文件 -->
<directory>src/main/webapp</directory>
<!--指定必须要放在此目录下才能被访问到-->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>