在Web开发中,会涉及到静态资源的访问支持、视图解析器的配置、转换器和格式化器的定制、文件上传下载等功能,甚至还需要考虑到与Web服务器关联的Servlet相关组件的定制,Spring Boot框架支持整合一些常用Web框架从而实现Web开发,并默认支持Web开发中的一些通用功能。
本章将对Spring Boot实现Web开发中涉及到的一些常用功能进行详细讲解。
在Spring Boot项目中,一旦引入了Web依赖启动器spring-boot-starter-web,那么Spring Boot整合Spring MVC框架默认实现的一些XxxAutoConfiguration自动配置类就会自动生效,几乎可以在无任何额外配置的情况下进行Web开发。
Spring Boot 整合Spring MVC的自动化配置功能特性
①内置了两个视图解析器:ContentNegotiatingViewResolver和BeanNameViewResolver;
②支持静态资源以及WebJars;
③自动注册了转换器和格式化器;
④支持Http消息转换器;
⑤自动注册了消息代码解析器;
⑥支持静态项目首页index.html;
⑦支持定制应用图标favicon.ico;
⑧自动初始化Web数据绑定器ConfigurableWebBindingInitializer。
下面通过一个具体的案例讲解Spring Boot整合Spring MVC框架实现Web开发的扩展功能。
步骤1:项目基础环境搭建
使用Spring Initializr方式创建名称为chapter05的Spring Boot项目,并在Dependencies依赖选择中选择Web模块下的web依赖启动器和Template Engines模块下的Thymeleaf依赖启动器。效果如图所示:
选择存储路径后点Finish完成项目创建。
步骤2:我们在charpter04项目上进一行开发,首先完成项目复制。
在IDEA中,找到chapter05项目里的src-main-java下内容删除
找到chapter04项目里的src-main-java下com文件夹,复制
在IDEA中,找到chapter05项目里的src-main-java,选择JAVA文件夹,Ctrl+V粘贴,如图:
步骤3:功能扩展实现
1、注册视图管理器,在config包下创建一个实现WebMvcConfigurer接口的配置类MyMVCconfig,用于对MVC框架功能扩展,MyMVCconfig.java代码如下:
package com.itheima.config;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //让下面的实现类声明为配置类,能被Spring Boot扫描到并生成实例存到容器中
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");
}
}
为了便于测试,我们把com.itheima.controller下的LoginController.java里的指向路径注释掉(RequestMapping("/toLoginPage"))(注释或取消注释快捷键:CTRL+SHIFT+/)
重新启动项目启动类,项目启动成功后,在浏览器上分别访问http://localhost:8080/toLoginPage和 http://localhost:8080/login.html, 两个均指向同一页面,效果如图:
可以看出,因为使用MyMVCconfig.java,所以两者访问一致,但也因为跳过使用LoginController.java,所以我们定义的public String toLoginPage(Model model)没有使用,则login.html中的${currentYear}"没有相关值 ,故显示为null
2、在config包下注册自定义拦截器MyInterceptor,实现HandlerInterceptor拦截器接口,MyInterceptor.java中编写如下方法:
package com.itheima.config;
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 {
//步骤1:重写preHandle方法,preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
@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");//跳转到toLoginPage
return false;
}
return true;
}
//重写postHandle,获得currentYear赋值
//postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
request.setAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
}
//afterCompletion无需进行操作,可不重写
}
3、在自定义配置类MyMVCconfig中,重写addInterceptors()方法注册自定义的拦截器,示例代码如下:
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/login.html");
}
方法中使用addPathPatterns("/**")方法拦载所有路径请求,交给myInterceptor拦截器处理,excludePathPatterns("/login.html")则是对login.html路径的请求进行了放行处理。
最后,重启项目,启动成功后,在浏览器上访问http://localhost:8080/admin:
可以看到,路径自动跳转到了用户登录页面,同时动态显示年份,说明此次定制的自定义拦截器生效。
需要说明的是,Spring Boot在整合Spring MVC过程中提供了许多默认自动化配置和特性,开发者可以通过Spring Boot提供的WebMvcConfigurer接口对MVC功能进行定制和扩展。如果开发者不想使用Spring Boot整合MVC时提供的一些默认配置,而是想要绝对的自定义管理,那么可以编写一个@Configuration注解配置类,同时添加@EnableWebMvc注解关闭Spring Boot提供的所有关于MVC功能的默认配置。
使用组件注册方式整合Servlet
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
步骤1:在项目chapter05中创建包com.itheima.servletComponent的包,并在该包下创建一个自定义Servlet类MyServlet.java,示例代码如下:
package com.itheima.servletComponent;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//步骤4:把实例类配置到spring容器中
@Component
//步骤1:继承HttpServlet
public class MyServlet extends HttpServlet {
//步骤2:重写doGet方法,技巧:输入doGet,找到后回车,再完善代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("Hello mysevlet");//借助resp对象输出一句话
}
//步骤3:重写doPost方法,技巧:输入doPost,找到后回车,再完善代码:调用doGet即可
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
步骤2:创建Servlet组件配置类。在项目com.itheima.config包下创建一个Servlet组件配置类SerlvetConfig,用来对Servlet相关组件进行注册。内容如下:
package com.itheima.config;
import com.itheima.servletComponent.MyServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//步骤3:添加@Configuration让SerlvetConfig作为配置类起作用
@Configuration
public class SerlvetConfig {
/*
* servlet组件的注册
* */
//步骤4:把当前方法getServlet的返回值存到容器中
@Bean
//步骤1:定义方法getservlet()对我们自定义的MyServlet进行注册
public ServletRegistrationBean getServlet(MyServlet myServlet){
//步骤2:在构造方法中为MyServlet构造映射路径并返回值
ServletRegistrationBean<MyServlet> myServletServletRegistrationBean = new ServletRegistrationBean<>(myServlet, "/myServlet");
return myServletServletRegistrationBean;
}
}
步骤3: 启动项目启动类,如图:
测试http://localhost:8080/myServlet,效果如图正常显示数据,说明Spring Boot整合Servlet成功:
使用组件注册方式整合过滤器Filter
步骤1:在com.itheima.servletComponent的包下创建一个类MyFilter.java,内容如下:
package com.itheima.servletComponent;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;
//步骤1:导入接口:implements Filter,注意不要导错包(javax.servlet.Filter)
//步骤2:在Filter后按Alt+Enter,选择implement methods,重写三个方法。
//步骤4:把实例类配置到spring容器中
@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 { //真正的业务逻辑方法
//步骤3:拦截后输入一句话,然后借助filterChain对象进行放行
System.out.println("myFilter执行了.....");//该方法拦截后执行一句执行
filterChain.doFilter(servletRequest,servletResponse);//借助filterChain对象进行放行
}
@Override
public void destroy() { //消毁方法
}
}
步骤2:在ServletConfig.java中向Servlet组件配置类注册自定义Filter类。示例代码如下:
//注册Filter组件
//步骤1:定义方法getFileter()对我们自定义的MyFilter进行注册
//步骤4:注意要加上@Bean
@Bean
public FilterRegistrationBean getFileter(MyFilter myFilter){
//步骤2:利用myFilter构造创建FilterRegistrationBean对象
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>(myFilter);
//步骤3:借助 filterFilterRegistrationBean的setUrlPatterns方法对"/toLoginPage"路径进行拦截
filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/toLoginPage"));
return filterFilterRegistrationBean;
}
}
步骤3:重启项目启动类,测试http://localhost:8080/toLoginPage,效果如图正常显示数据,说明Spring Boot整合自定义Filter组件成功.
使用组件注册方式整合监听器Listener
步骤1:在com.itheima.servletComponent的包下创建一个类MyListener.java,内容如下:
package com.itheima.servletComponent;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
//步骤1:导入接口:implements Filter,注意不要导错包(javax.servlet.Filter)
//ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期
//步骤3:把MyListener类作为组件配置到spring容器中
@Component
public class MyListener implements ServletContextListener {
//步骤2:重写contextInitialized和contextDestroyed方法。各输入一句话说明相关方法已执行
@Override
//当Servlet 容器启动Web 应用时调用contextInitialized方法。在调用完该方法之后,容器再对Filter 初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized执行了。。。。");
}
@Override
//当Servlet 容器终止Web 应用时调用contextDestroyed方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器。
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed执行了。。。。");
}
}
需要说明的是:Servlet容器提供了很多Listener接口,我们在自定义Listener类时根据自身需求选择实现对应接口即可。
步骤2:在ServletConfig.java中向Servlet组件配置类注册自定义Listener类。示例代码如下:
//注册Listener组件
//步骤1:定义方法getListener()对我们自定义的MyListener进行注册
//步骤3:注意要加上@Bean
@Bean
public ServletListenerRegistrationBean getListener(MyListener myListener){
//步骤2:利用myListener构造创建ServletListenerRegistrationBean对象:new ServletListenerRegistrationBean<>(myListener),按Alt+Enter,选择Introduce local variable
ServletListenerRegistrationBean<MyListener> myListenerServletListenerRegistrationBean = new ServletListenerRegistrationBean<>(myListener);
return myListenerServletListenerRegistrationBean;
}
步骤3:重启项目启动类,可以在控制台看到执行效果:
点击运行左下角的Exit按钮,关闭当前项目(注意,如果 直接单击红色按钮会强制关闭程序,浏览器就无法打印关闭监听信息),再次查看控制台,可看到输出语句:contextDestroyed执行了。。。。
使用ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBean组件组装配置的根本目的是对一些请求路径和参数进行初始化设置和组装。假设没有组件注册类,那么自定义Servlet虽然生效,但无法确定是哪个访问路径生效。自定义的Filter会对所有的请求都进行过滤,不会出现选择性过滤的情况。而自定义的Listener则没有太大影响,因为定制该组件基本不需要设置什么参数。
为了简化操作,我们在5.2.1小节自定义组件的基础上使用路径扫描的方式实现Servlet容器的Servlet,Filter和Listener三大组件的整合。操作步骤:
步骤1:要避免与之前的使用组件注册的方式相互干扰删除
步骤2:
给MyServlet组件类添加:@WebServlet("/myServlet")
说明:当请求该Servlet时,服务器就会自动读取当中的信息,如果注解@WebServlet("/myServlet"),则表示该Servlet默认的请求路径为…/myServlet.
给MyListener组件类添加:@WebListener
说明:@WebListeneret注解用来标注此为Listener类.
给MyFilter组件类添加:@WebFilter(value = {"/toLoginPage","/abc"})
说明:实现对两路径"/toLoginPage","/abc"进行拦截。
步骤3:在项目主程序启动类上添加注解:@ServletComponentScan,开启基于注解方式的Servlet组件扫描支持。
步骤4:启动项目主程序
控制台打印了:contextInitialized执行了。。。。,证明MyListener已启作用
访问:http://localhost:8080/myServlet,显示Hello mysevlet,证明MyServlet已成功。
访问:http://localhost:8080/toLoginPage,可以看到跳转到登陆页面,证明MyFilter成功
同时可以看到控制台打印了:myFilter执行了…,说明MyFilter拦截成功。
整体效果如图:
开发Web应用时,文件上传是常见的需求,浏览器通过表单形式将文件以流的形式传递 给服务器,服务器对上传的数据解析处理。我们在charpter05项目中通过一个案例解析如何 使用 Spring Boot实现文件上传。本现本小节需要upload.html,download.html和js文档的可以通过百度云进行下载,网址:https://pan.baidu.com/s/1yJg0NcHRZ_XoTqXA_g2EAQ
具体步骤如下:
步骤1:编写文件上传的表单页面,这里引入upload.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">
//注意上传jquery.min.js文件
<script th:src="@{/login/js/jquery.min.js}">script>
head>
<body>
// 下一行判断uploadStatus是否有值,默认值为上传成功,如果uploadStatus是动态的,则会覆盖上传成功
<div th:if="${uploadStatus}" style="color: red" th:text="${uploadStatus}">上传成功div>
//表单三要素,必须要有文件上传项,2提交方式必须是post,3 enctype必须是"multipart/form-data"
<form th:action="@{/uploadFile}" method="post" enctype="multipart/form-data">
上传文件: <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 = "";
innerdiv += "" +
"";
innerdiv +="";
$("#file").append(innerdiv);
// 打开上传按钮
$("#submit").css("display","block");
}
// 删除当前行
function remove(obj) {
$(obj).parent().remove();
if($("#file div").length ==0){
$("#submit").css("display","none");
}
}
script>
body>
html>
-
步骤2:在resources/static/login/中添加js文件夹,引入jquery.min.js文件。
-
步骤3:如果需要设置文件上传的相关配置,可在全局配置文件中(application.properties)添加相关代码,如:
# 单个上传文件大小限制(默认1MB)
spring.servlet.multipart.max-file-size=10MB
# 总上传文件大小限制(默认10MB)
spring.servlet.multipart.max-request-size=50MB
-
步骤4:进行文件上传处理,实现文件上传功能
-
在包com.itheima.controller下创建一个管理文件上传下载的控制类FileController.java,用于实现文件上传功能,内容如下:
package com.itheima.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.io.IOException;
import java.util.UUID;
@Controller //表明了这个类是一个控制器类。注入服务
public class FileController {
/*
* 跳转到upload.html
* */
@GetMapping("/toUpload")//下面方法的请求地址
public String toUpload(){
return "upload";//返回到upload.html页面
}
/*
* 实现文件上传
* */
@PostMapping("/uploadFile")//设置当前访问地址跟upload.html中form表单的跳转地址保持一致
//参数为上传的文件,fileUpload为upload.html文件中设置的文件名变理
public String uploadFile(MultipartFile[] fileUpload, Model model){
//返回上传成功的状态信息,可在前或后
model.addAttribute("uploadStatus", "上传成功");
//上传文件逻辑开始
for (MultipartFile file : fileUpload) {
//获取上传文件的名称及后缀名,注意:file.getOriginalFilename() 获取上传的文件名称,但是在IE11/Edge浏览器下面,获取到的路径信息带有盘符
String originalFilename = file.getOriginalFilename();
//修复去盘符的办法
// 检查Unix样式的路径
int unixSep = originalFilename.lastIndexOf('/');
// 检查Windows样式的路径
int winSep = originalFilename.lastIndexOf('\\');
// Cut off at latest possible point
int pos = (winSep > unixSep ? winSep : unixSep);
if (pos != -1) {
// Any sort of path separator found...
originalFilename = originalFilename.substring(pos + 1);
}
//修复去盘符的办法结束
//重新生成文件名
String fileName= UUID.randomUUID()+"-"+originalFilename;
//设置存储目录
String dirPath="D:/file/";
//如果文件不存在,还要创建
File file1= new File(dirPath);
if(!file1.exists()){
file1.mkdirs();
}
try{
file.transferTo(new File(dirPath+fileName));
}catch (IOException e){
e.printStackTrace();
model.addAttribute("uploadStatus","上传失败");
}
}
//携带上传状态信息跳转到upload.html
return "upload";
}
}
-
步骤5:重启主程序,访问:http://localhost:8080/toUpload,会跳转到upload.html页面。说明FileController类的toUpload()方法跳转成功。点击“添加文件”按钮,触发add()方法,如图:
这里选择一个“天数.txt”文件,点击“上传”,则会请求到FileController.java里的uploadFile()方法,返回“上传成功”的状态信息。
在电脑D盘下可找到File文件夹,里面有我们上传的文件。
3.2 文件下载
下载文件能够通过IO流实现,所以多数框架并没有对文件下载进行封装处理。文件下载涉及到不同浏览器的解析处理,可能会出现中文自己乱码的情况。下面,分别针对英文名文件和中文名文件进行讲解。
- 英文名文件下载
-
步骤1:pom.xml文件中引入添加依赖
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
-
步骤2:在templates文件夹下创建一个演示下载的download.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.jpgtd>
<td><a th:href="@{/download(filename='bloglogo.jpg')}">下载文件a>td>
tr>
<tr>
<td>Python编程从入门到实践.pdftd>
<td><a th:href="@{/download(filename='Python编程从入门到实践.pdf')}">
下载文件a>td>
tr>
table>
body>
html>
-
步骤3:在D:\file路径中放置要提供下载的两个文件bloglogo.jpg和Python编程从入门到实践.pdf
-
步骤4:FileController中编写文件下载方法到FileController类的定义中
/*文件下载方法一:跳转到download.html*/
@GetMapping("/toDownload") //设置请求地址
public String toDownload(){
return "download";
}
/*
* 文件下载方法二:文件下载代码
* */
@GetMapping("/download")//下面方法的请求地址
//返回值 byte数组,参数:filename跟download.html中的下载里传递的参数一致。
public ResponseEntity<byte[]> fileDownload(String filename){
//指定要下载的文件路径
String dirPath="D:/file/";
//创建文件对象
File file = new File(dirPath + File.separator + filename);
//设置响应头
HttpHeaders httpHeaders = new HttpHeaders();
//通知浏览器以下载的形式打开
httpHeaders.setContentDispositionFormData("attachment",filename);
//定义以流的形式下载返回文件数据
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK)中readFileToByteArray显示有异常
//按Alt+Enter,选择Surrond with try/catch,添加try/catch,当下载失败时添加处理
try {
return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
//下载失败的处理
return new ResponseEntity<>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
}
}
-
步骤5:重启主程序,效果如图:
访问:http://localhost:8080/toDownload,效果如图:
可以看到跳转到了download.html页面。下载两个文件,可能看到下载的文件中,中文丢失。
2.中文名文件下载改进
-
步骤6:在FileController类中,添加以下代码,根据不同的浏览器对下载的中文名进行转码:
//根据浏览器的不同进行编码设置,返回编码后的文件名
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");
}
-
步骤7:引用转码方法,FileController类中文件下载代码修改如下:
/*
* 文件下载方法二:文件下载代码
* */
@GetMapping("/download")//下面方法的请求地址
//返回值 byte数组,参数:filename跟download.html中的下载里传递的参数一致。
//fileDownload(String filename)方法用来处理路径为“/download”的Get请求,并进行文件下载处理。
public ResponseEntity fileDownload(String filename,HttpServletRequest request) throws Exception {//因调用中文转码的request,参数补充:HttpServletRequest request,并对异常进行抛出:throws Exception
//指定要下载的文件路径
String dirPath="D:/file/";
//创建文件对象
File file = new File(dirPath + File.separator + filename);
//设置响应头
HttpHeaders httpHeaders = new HttpHeaders();
//通知浏览器以下载的形式打开(下载前对文件名进行转码)
filename=getFilename(request,filename);//中文转码补充
httpHeaders.setContentDispositionFormData("attachment",filename);
//定义以流的形式下载返回文件数据
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK)中readFileToByteArray显示有异常
//按Alt+Enter,选择Surrond with try/catch,添加try/catch,当下载失败时添加处理
try {
//获取下载结果时,使用了FileUtils的readFileToByteArray()方法快速下载文件,并以ResponseEntity类型数据返回。
return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
//下载失败的处理
return new ResponseEntity<>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
}
}
-
步骤8: 重启主程序,重新下载文件,中文下载正常。
4 Spring Boot 应用的打包和部署
传统的Web应用在发布之前通常会打成War包,然后将War包部署到Tomact等容器中使用,而Spring Boot应用使用的是嵌入式Servlet容器,默认能以Jar包的形式部署,如果要以War包的形式部署,就需要进行一些配置。下面以chapter05项目分别介绍 两种形式进行打包和部署的方法。
4.1 Jar包方式打包部署
-
步骤1:在项目pom.xml文件中添加Maven打包插件,如果项目已选Spring Web进行项目创建时,会自动加上打包插件。打包插件代码如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
-
步骤2:使用IDEA开发工具进行打包。
-
首先单击界面左下角的小窗口图标,打开界面右侧边框视图栏
-
单击右侧边框的Maven视图,打开对应的项目操作窗口
-
选择项目目录下的Lifecycle目录中的Package选项
-
直接双击就可以进行项目打包了。
解压查看打成的Jar包,BOOT-INF目录下有lib和classes两个目录文件。其中,lib目录下对庆着所有添加的依赖文件导入的jar文件。classes目录下对应着项目打包编译后的所有文件。
-
步骤3:Jar包方式部署
这里借助IDEA开发工具快速进行Jar包项目的部署
-
先在IDEA开发工具控制台上单击切换到Terminal终端界面,该界面会默认打开项目所在位置;
-
然后在项目所在位置后的指令输入提示位置直接使用“java -jar xxx.jar”的指令部署启动Jar包。如图:
- 回车执行上述指令后,Terminal终端界面就会启动Spring Boot项目。效果如图:
至此,可以通过浏览器访问之前的文件上传下载等操作。按CTRL+C 可以退出Terminal终端运行。
4.2 War包方式打包部署
关于Tomcat 的安装,请参考:https://jingyan.baidu.com/article/e8cdb32be15b0437052bad17.html
这里我们介绍下载的免安装包使用方法:
1、配置好环境变量
- 鼠标右键点击【这台电脑】–>选择【属性】->【高级系统设置】–>【系统变量】
- 然后,点击“系统变量”框下的【新建】按钮
- 变量名:CATALINA_BASE 变量值:D:\apache-tomcat-9.0.16(Tomcat安装位置,即刚刚解压文件夹下bin文件所在位置)
- 新建CATALINA_HOME变量 变量名:CATALINA_HOME 变量值:D:\apache-tomcat-9.0.16(变量值同上,填Tomcat的安装位置)
- 找到Path变量,点击编辑,新增变量名:Path 变量值: %CATALINA_HOME%\bin(不要删除原有path变量值,直接在最后一行添加上这一句即可)
2.打开cmd命令提示符,输入startup后回车,就可以看到Tomcat成功启动了。(注意,上一小节如果还在Terminal中运行占了8080端口,启动会失败)
关于tomcat端口的修改使用,请参考:https://jingyan.baidu.com/article/84b4f565a94d5c60f6da32e5.html
tomcat启动后访问默认端口:http://localhostd:8080, 效果如图:
注意,这里暂不用启动。
- 步骤1:在chapter05项目中,打开pom.xml,在声明打包方式为war包,如图:
-
步骤2:在pom.xml的中声明使用外部的Tomcat服务器
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
-
步骤3:提供Spring Boot启动的Servlet初始化器
主程序启动类继承SpringBootServletInitializer类并实现configure()方法,在configure()方法中,sources(Chapter05Application.class,args)方法的首个参数必须是项目主程序启动类。
需要说明的是,为Spring Boot提供启动的Servlet初始化器SpringBootServletInitializer的典型的做法就是让主程序启动类继承SpringBootServletInitializer类并实现configure()方法;除此之外,还可以在项目中单独提供一个继承SpringBootServletInitializer的子类,并实现configure()方法。这里chapter05Application的改动如下:
@SpringBootApplication//开启基于注解方式的Servlet组件扫描支持
@ServletComponentScan
public class Chapter05Application extends SpringBootServletInitializer {
//程序主类继承SpringBootServletInitializer,并重写configure()方法
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Chapter05Application.class);
}
-
步骤4:同样双击package进行打包,如图:
完成后的打包文件为war后缀。
-
步骤5:将打包好的War包复制到Tomcat安装目录下的webapps目录中,Tomcat启动时会解压webapps下的war包
-
步骤6:执行Tomcat的bin目录下的startup.bat,启动Tomcat,效果如图:
可以看到,war包已解压
访问之前的上传方法,需要加上解压后的文件名,如图:
至此,完成war布署。